Merge branch 'develop' into mkjs-n

This commit is contained in:
tamaina 2023-05-25 14:49:51 +00:00
commit d7fe783c46
27 changed files with 3254 additions and 1002 deletions

72
locales/generateDTS.js Normal file
View File

@ -0,0 +1,72 @@
const fs = require('fs');
const yaml = require('js-yaml');
const ts = require('typescript');
function createMembers(record) {
return Object.entries(record)
.map(([k, v]) => ts.factory.createPropertySignature(
undefined,
ts.factory.createStringLiteral(k),
undefined,
typeof v === 'string'
? ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
: ts.factory.createTypeLiteralNode(createMembers(v)),
));
}
module.exports = function generateDTS() {
const locale = yaml.load(fs.readFileSync(`${__dirname}/ja-JP.yml`, 'utf-8'));
const members = createMembers(locale);
const elements = [
ts.factory.createInterfaceDeclaration(
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
ts.factory.createIdentifier('Locale'),
undefined,
undefined,
members,
),
ts.factory.createVariableStatement(
[ts.factory.createToken(ts.SyntaxKind.DeclareKeyword)],
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier('locales'),
undefined,
ts.factory.createTypeLiteralNode([ts.factory.createIndexSignature(
undefined,
[ts.factory.createParameterDeclaration(
undefined,
undefined,
ts.factory.createIdentifier('lang'),
undefined,
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
undefined,
)],
ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier('Locale'),
undefined,
),
)]),
undefined,
)],
ts.NodeFlags.Const | ts.NodeFlags.Ambient | ts.NodeFlags.ContextFlags,
),
),
ts.factory.createExportAssignment(
undefined,
true,
ts.factory.createIdentifier('locales'),
),
];
const printed = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed,
}).printList(
ts.ListFormat.MultiLine,
ts.factory.createNodeArray(elements),
ts.createSourceFile('index.d.ts', '', ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS),
);
fs.writeFileSync(`${__dirname}/index.d.ts`, `/* eslint-disable */
// This file is generated by locales/generateDTS.js
// Do not edit this file directly.
${printed}`, 'utf-8');
}

2145
locales/index.d.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -59,7 +59,7 @@
"@typescript-eslint/eslint-plugin": "5.59.5", "@typescript-eslint/eslint-plugin": "5.59.5",
"@typescript-eslint/parser": "5.59.5", "@typescript-eslint/parser": "5.59.5",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "12.12.0", "cypress": "12.13.0",
"eslint": "8.40.0", "eslint": "8.40.0",
"start-server-and-test": "2.0.0" "start-server-and-test": "2.0.0"
}, },

View File

@ -63,9 +63,9 @@
"@fastify/multipart": "7.6.0", "@fastify/multipart": "7.6.0",
"@fastify/static": "6.10.1", "@fastify/static": "6.10.1",
"@fastify/view": "7.4.1", "@fastify/view": "7.4.1",
"@nestjs/common": "9.4.1", "@nestjs/common": "9.4.2",
"@nestjs/core": "9.4.1", "@nestjs/core": "9.4.2",
"@nestjs/testing": "9.4.1", "@nestjs/testing": "9.4.2",
"@peertube/http-signature": "1.7.0", "@peertube/http-signature": "1.7.0",
"@sinonjs/fake-timers": "10.0.2", "@sinonjs/fake-timers": "10.0.2",
"@swc/cli": "0.1.62", "@swc/cli": "0.1.62",
@ -179,11 +179,11 @@
"@types/jsonld": "1.5.8", "@types/jsonld": "1.5.8",
"@types/jsrsasign": "10.5.8", "@types/jsrsasign": "10.5.8",
"@types/mime-types": "2.1.1", "@types/mime-types": "2.1.1",
"@types/node": "20.2.1", "@types/node": "20.2.3",
"@types/node-fetch": "3.0.3", "@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.8", "@types/nodemailer": "6.4.8",
"@types/oauth": "0.9.1", "@types/oauth": "0.9.1",
"@types/pg": "8.6.6", "@types/pg": "8.10.1",
"@types/pug": "2.0.6", "@types/pug": "2.0.6",
"@types/punycode": "2.1.0", "@types/punycode": "2.1.0",
"@types/qrcode": "1.5.0", "@types/qrcode": "1.5.0",
@ -197,7 +197,7 @@
"@types/sinonjs__fake-timers": "8.1.2", "@types/sinonjs__fake-timers": "8.1.2",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3", "@types/tmp": "0.2.3",
"@types/unzipper": "0.10.5", "@types/unzipper": "0.10.6",
"@types/uuid": "9.0.1", "@types/uuid": "9.0.1",
"@types/vary": "1.1.0", "@types/vary": "1.1.0",
"@types/web-push": "3.3.2", "@types/web-push": "3.3.2",

View File

@ -4,7 +4,7 @@ import * as Redis from 'ioredis';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { MeiliSearch } from 'meilisearch'; import { MeiliSearch } from 'meilisearch';
import { DI } from './di-symbols.js'; import { DI } from './di-symbols.js';
import { loadConfig } from './config.js'; import { Config, loadConfig } from './config.js';
import { createPostgresDataSource } from './postgres.js'; import { createPostgresDataSource } from './postgres.js';
import { RepositoryModule } from './models/RepositoryModule.js'; import { RepositoryModule } from './models/RepositoryModule.js';
import type { Provider, OnApplicationShutdown } from '@nestjs/common'; import type { Provider, OnApplicationShutdown } from '@nestjs/common';
@ -25,7 +25,7 @@ const $db: Provider = {
const $meilisearch: Provider = { const $meilisearch: Provider = {
provide: DI.meilisearch, provide: DI.meilisearch,
useFactory: (config) => { useFactory: (config: Config) => {
if (config.meilisearch) { if (config.meilisearch) {
return new MeiliSearch({ return new MeiliSearch({
host: `${config.meilisearch.ssl ? 'https' : 'http' }://${config.meilisearch.host}:${config.meilisearch.port}`, host: `${config.meilisearch.ssl ? 'https' : 'http' }://${config.meilisearch.host}:${config.meilisearch.port}`,
@ -40,7 +40,7 @@ const $meilisearch: Provider = {
const $redis: Provider = { const $redis: Provider = {
provide: DI.redis, provide: DI.redis,
useFactory: (config) => { useFactory: (config: Config) => {
return new Redis.Redis({ return new Redis.Redis({
port: config.redis.port, port: config.redis.port,
host: config.redis.host, host: config.redis.host,
@ -55,7 +55,7 @@ const $redis: Provider = {
const $redisForPub: Provider = { const $redisForPub: Provider = {
provide: DI.redisForPub, provide: DI.redisForPub,
useFactory: (config) => { useFactory: (config: Config) => {
const redis = new Redis.Redis({ const redis = new Redis.Redis({
port: config.redisForPubsub.port, port: config.redisForPubsub.port,
host: config.redisForPubsub.host, host: config.redisForPubsub.host,
@ -71,7 +71,7 @@ const $redisForPub: Provider = {
const $redisForSub: Provider = { const $redisForSub: Provider = {
provide: DI.redisForSub, provide: DI.redisForSub,
useFactory: (config) => { useFactory: (config: Config) => {
const redis = new Redis.Redis({ const redis = new Redis.Redis({
port: config.redisForPubsub.port, port: config.redisForPubsub.port,
host: config.redisForPubsub.host, host: config.redisForPubsub.host,

View File

@ -160,37 +160,41 @@
<path d="M12 9v2m0 4v.01"></path> <path d="M12 9v2m0 4v.01"></path>
<path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path> <path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path>
</svg> </svg>
<h1>An error has occurred!</h1> <h1>Failed to load<br>読み込みに失敗しました</h1>
<button class="button-big" onclick="location.reload();"> <button class="button-big" onclick="location.reload(true);">
<span class="button-label-big">Refresh</span> <span class="button-label-big">Reload / リロード</span>
</button> </button>
<p class="dont-worry">Don't worry, it's (probably) not your fault.</p> <p><b>The following actions may solve the problem. / 以下を行うと解決する可能性があります</b></p>
<p>If the problem persists after refreshing, please contact your instance's administrator.<br>You may also try the following options:</p> <p>Clear the browser cache / ブラウザのキャッシュをクリアする</p>
<p>Update your os and browser.</p> <p>Update your os and browser / ブラウザおよびOSを最新バージョンに更新する</p>
<p>Disable an adblocker.</p> <p>Disable an adblocker / アドブロッカーを無効にする</p>
<a href="/flush"> <details style="color: #86b300;">
<button class="button-small"> <summary>Other options / その他のオプション</summary>
<span class="button-label-small">Clear preferences and cache</span> <a href="/flush">
</button> <button class="button-small">
</a> <span class="button-label-small">Clear preferences and cache</span>
<br> </button>
<a href="/cli"> </a>
<button class="button-small"> <br>
<span class="button-label-small">Start the simple client</span> <a href="/cli">
</button> <button class="button-small">
</a> <span class="button-label-small">Start the simple client</span>
<br> </button>
<a href="/bios"> </a>
<button class="button-small"> <br>
<span class="button-label-small">Start the repair tool</span> <a href="/bios">
</button> <button class="button-small">
</a> <span class="button-label-small">Start the repair tool</span>
</button>
</a>
</details>
<br> <br>
<div id="errors"></div> <div id="errors"></div>
`; `;
errorsElement = document.getElementById('errors'); errorsElement = document.getElementById('errors');
} }
const detailsElement = document.createElement('details'); const detailsElement = document.createElement('details');
detailsElement.id = 'errorInfo';
detailsElement.innerHTML = ` detailsElement.innerHTML = `
<br> <br>
<summary> <summary>
@ -247,7 +251,7 @@
.button-label-big { .button-label-big {
color: #222; color: #222;
font-weight: bold; font-weight: bold;
font-size: 20px; font-size: 1.2em;
padding: 12px; padding: 12px;
} }
@ -267,11 +271,6 @@
font-size: 16px; font-size: 16px;
} }
.dont-worry,
#msg {
font-size: 18px;
}
.icon-warning { .icon-warning {
color: #dec340; color: #dec340;
height: 4rem; height: 4rem;
@ -279,14 +278,15 @@
} }
h1 { h1 {
font-size: 32px; font-size: 1.5em;
margin: 1em;
} }
code { code {
font-family: Fira, FiraCode, monospace; font-family: Fira, FiraCode, monospace;
} }
details { #errorInfo {
background: #333; background: #333;
margin-bottom: 2rem; margin-bottom: 2rem;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
@ -296,16 +296,16 @@
margin: auto; margin: auto;
} }
summary { #errorInfo summary {
cursor: pointer; cursor: pointer;
} }
summary > * { #errorInfo summary > * {
display: inline; display: inline;
} }
@media screen and (max-width: 500px) { @media screen and (max-width: 500px) {
details { #errorInfo {
width: 50%; width: 50%;
} }
`) `)

View File

@ -22,7 +22,7 @@
"@syuilo/aiscript": "0.13.3", "@syuilo/aiscript": "0.13.3",
"@tabler/icons-webfont": "2.17.0", "@tabler/icons-webfont": "2.17.0",
"@vitejs/plugin-vue": "4.2.3", "@vitejs/plugin-vue": "4.2.3",
"@vue-macros/reactivity-transform": "0.3.7", "@vue-macros/reactivity-transform": "0.3.8",
"@vue/compiler-sfc": "3.3.4", "@vue/compiler-sfc": "3.3.4",
"autosize": "6.0.1", "autosize": "6.0.1",
"broadcast-channel": "4.20.2", "broadcast-channel": "4.20.2",
@ -53,7 +53,7 @@
"punycode": "2.3.0", "punycode": "2.3.0",
"querystring": "0.2.1", "querystring": "0.2.1",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"rollup": "3.22.0", "rollup": "3.23.0",
"s-age": "1.1.2", "s-age": "1.1.2",
"sanitize-html": "2.10.0", "sanitize-html": "2.10.0",
"sass": "1.62.1", "sass": "1.62.1",
@ -77,37 +77,37 @@
"vuedraggable": "next" "vuedraggable": "next"
}, },
"devDependencies": { "devDependencies": {
"@storybook/addon-actions": "7.0.12", "@storybook/addon-actions": "7.0.15",
"@storybook/addon-essentials": "7.0.12", "@storybook/addon-essentials": "7.0.15",
"@storybook/addon-interactions": "7.0.12", "@storybook/addon-interactions": "7.0.15",
"@storybook/addon-links": "7.0.12", "@storybook/addon-links": "7.0.15",
"@storybook/addon-storysource": "7.0.12", "@storybook/addon-storysource": "7.0.15",
"@storybook/addons": "7.0.12", "@storybook/addons": "7.0.15",
"@storybook/blocks": "7.0.12", "@storybook/blocks": "7.0.15",
"@storybook/core-events": "7.0.12", "@storybook/core-events": "7.0.15",
"@storybook/jest": "0.1.0", "@storybook/jest": "0.1.0",
"@storybook/manager-api": "7.0.12", "@storybook/manager-api": "7.0.15",
"@storybook/preview-api": "7.0.12", "@storybook/preview-api": "7.0.15",
"@storybook/react": "7.0.12", "@storybook/react": "7.0.15",
"@storybook/react-vite": "7.0.12", "@storybook/react-vite": "7.0.15",
"@storybook/testing-library": "0.1.0", "@storybook/testing-library": "0.1.0",
"@storybook/theming": "7.0.12", "@storybook/theming": "7.0.15",
"@storybook/types": "7.0.12", "@storybook/types": "7.0.15",
"@storybook/vue3": "7.0.12", "@storybook/vue3": "7.0.15",
"@storybook/vue3-vite": "7.0.12", "@storybook/vue3-vite": "7.0.15",
"@testing-library/jest-dom": "5.16.5", "@testing-library/jest-dom": "5.16.5",
"@testing-library/vue": "7.0.0", "@testing-library/vue": "7.0.0",
"@types/escape-regexp": "0.0.1", "@types/escape-regexp": "0.0.1",
"@types/estree": "1.0.1", "@types/estree": "1.0.1",
"@types/gulp": "4.0.10", "@types/gulp": "4.0.10",
"@types/gulp-rename": "2.0.2", "@types/gulp-rename": "2.0.2",
"@types/matter-js": "0.18.3", "@types/matter-js": "0.18.4",
"@types/micromatch": "4.0.2", "@types/micromatch": "4.0.2",
"@types/node": "20.2.1", "@types/node": "20.2.3",
"@types/punycode": "2.1.0", "@types/punycode": "2.1.0",
"@types/sanitize-html": "2.9.0", "@types/sanitize-html": "2.9.0",
"@types/seedrandom": "3.0.5", "@types/seedrandom": "3.0.5",
"@types/testing-library__jest-dom": "^5.14.5", "@types/testing-library__jest-dom": "^5.14.6",
"@types/throttle-debounce": "5.0.0", "@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
"@types/uuid": "9.0.1", "@types/uuid": "9.0.1",
@ -117,13 +117,13 @@
"@typescript-eslint/parser": "5.59.5", "@typescript-eslint/parser": "5.59.5",
"@vitest/coverage-c8": "0.31.1", "@vitest/coverage-c8": "0.31.1",
"@vue/runtime-core": "3.3.4", "@vue/runtime-core": "3.3.4",
"astring": "1.8.4", "astring": "1.8.5",
"chokidar-cli": "3.0.0", "chokidar-cli": "3.0.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "12.12.0", "cypress": "12.13.0",
"eslint": "8.40.0", "eslint": "8.40.0",
"eslint-plugin-import": "2.27.5", "eslint-plugin-import": "2.27.5",
"eslint-plugin-vue": "9.13.0", "eslint-plugin-vue": "9.14.0",
"fast-glob": "3.2.12", "fast-glob": "3.2.12",
"happy-dom": "9.19.2", "happy-dom": "9.19.2",
"micromatch": "3.1.10", "micromatch": "3.1.10",
@ -133,7 +133,7 @@
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"start-server-and-test": "2.0.0", "start-server-and-test": "2.0.0",
"storybook": "7.0.12", "storybook": "7.0.15",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"summaly": "github:misskey-dev/summaly", "summaly": "github:misskey-dev/summaly",
"vite-plugin-turbosnap": "1.0.2", "vite-plugin-turbosnap": "1.0.2",

View File

@ -5,7 +5,9 @@ import '@/style.scss';
import { mainBoot } from './boot/main-boot'; import { mainBoot } from './boot/main-boot';
import { subBoot } from './boot/sub-boot'; import { subBoot } from './boot/sub-boot';
if (['/share', '/auth', '/miauth'].includes(location.pathname)) { const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete'];
if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) {
subBoot(); subBoot();
} else { } else {
mainBoot(); mainBoot();

View File

@ -8,27 +8,28 @@
> >
<template #header>{{ i18n.ts.forgotPassword }}</template> <template #header>{{ i18n.ts.forgotPassword }}</template>
<form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit"> <MkSpacer :marginMin="20" :marginMax="28">
<div class="main _gaps_m"> <form v-if="instance.enableEmail" @submit.prevent="onSubmit">
<MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required> <div class="_gaps_m">
<template #label>{{ i18n.ts.username }}</template> <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required>
<template #prefix>@</template> <template #label>{{ i18n.ts.username }}</template>
</MkInput> <template #prefix>@</template>
</MkInput>
<MkInput v-model="email" type="email" :spellcheck="false" required> <MkInput v-model="email" type="email" :spellcheck="false" required>
<template #label>{{ i18n.ts.emailAddress }}</template> <template #label>{{ i18n.ts.emailAddress }}</template>
<template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template> <template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template>
</MkInput> </MkInput>
<MkButton type="submit" :disabled="processing" primary style="margin: 0 auto;">{{ i18n.ts.send }}</MkButton> <MkButton type="submit" rounded :disabled="processing" primary style="margin: 0 auto;">{{ i18n.ts.send }}</MkButton>
<MkInfo>{{ i18n.ts._forgotPassword.ifNoEmail }}</MkInfo>
</div>
</form>
<div v-else>
{{ i18n.ts._forgotPassword.contactAdmin }}
</div> </div>
<div class="sub"> </MkSpacer>
<MkA to="/about" class="_link">{{ i18n.ts._forgotPassword.ifNoEmail }}</MkA>
</div>
</form>
<div v-else class="bafecedb">
{{ i18n.ts._forgotPassword.contactAdmin }}
</div>
</MkModalWindow> </MkModalWindow>
</template> </template>
@ -37,6 +38,7 @@ import { } from 'vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os'; import * as os from '@/os';
import { instance } from '@/instance'; import { instance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
@ -62,20 +64,3 @@ async function onSubmit() {
dialog.close(); dialog.close();
} }
</script> </script>
<style lang="scss" scoped>
.bafeceda {
> .main {
padding: 24px;
}
> .sub {
border-top: solid 0.5px var(--divider);
padding: 24px;
}
}
.bafecedb {
padding: 24px;
}
</style>

View File

@ -1,16 +1,16 @@
<template> <template>
<div v-show="props.modelValue.length != 0" class="skeikyzd"> <div v-show="props.modelValue.length != 0" :class="$style.root">
<Sortable :modelValue="props.modelValue" class="files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)"> <Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)">
<template #item="{element}"> <template #item="{element}">
<div class="file" @click="showFileMenu(element, $event)" @contextmenu.prevent="showFileMenu(element, $event)"> <div :class="$style.file" @click="showFileMenu(element, $event)" @contextmenu.prevent="showFileMenu(element, $event)">
<MkDriveFileThumbnail :data-id="element.id" class="thumbnail" :file="element" fit="cover"/> <MkDriveFileThumbnail :data-id="element.id" :class="$style.thumbnail" :file="element" fit="cover"/>
<div v-if="element.isSensitive" class="sensitive"> <div v-if="element.isSensitive" :class="$style.sensitive">
<i class="ti ti-alert-triangle icon"></i> <i class="ti ti-alert-triangle" style="margin: auto;"></i>
</div> </div>
</div> </div>
</template> </template>
</Sortable> </Sortable>
<p class="remain">{{ 16 - props.modelValue.length }}/16</p> <p :class="$style.remain">{{ 16 - props.modelValue.length }}/16</p>
</div> </div>
</template> </template>
@ -108,60 +108,53 @@ function showFileMenu(file, ev: MouseEvent) {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.skeikyzd { .root {
padding: 8px 16px; padding: 8px 16px;
position: relative; position: relative;
}
> .files { .files {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
}
> .file { .file {
position: relative; position: relative;
width: 64px; width: 64px;
height: 64px; height: 64px;
margin-right: 4px; margin-right: 4px;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
cursor: move; cursor: move;
}
&:hover > .remove { .thumbnail {
display: block; width: 100%;
} height: 100%;
z-index: 1;
color: var(--fg);
}
> .thumbnail { .sensitive {
width: 100%; display: flex;
height: 100%; position: absolute;
z-index: 1; width: 64px;
color: var(--fg); height: 64px;
} top: 0;
left: 0;
z-index: 2;
background: rgba(17, 17, 17, .7);
color: #fff;
}
> .sensitive { .remain {
display: flex; display: block;
position: absolute; position: absolute;
width: 64px; top: 8px;
height: 64px; right: 8px;
top: 0; margin: 0;
left: 0; padding: 0;
z-index: 2; font-size: 90%;
background: rgba(17, 17, 17, .7);
color: #fff;
> .icon {
margin: auto;
}
}
}
}
> .remain {
display: block;
position: absolute;
top: 8px;
right: 8px;
margin: 0;
padding: 0;
}
} }
</style> </style>

View File

@ -1,7 +1,7 @@
<template> <template>
<div ref="rootEl" class="meijqfqm"> <div ref="rootEl" :class="$style.root">
<canvas :id="idForCanvas" ref="canvasEl" class="canvas" :width="width" height="300" @contextmenu.prevent="() => {}"></canvas> <canvas :id="idForCanvas" ref="canvasEl" style="display: block;" :width="width" height="300" @contextmenu.prevent="() => {}"></canvas>
<div :id="idForTags" ref="tagsEl" class="tags"> <div :id="idForTags" ref="tagsEl" :class="$style.tags">
<ul> <ul>
<slot></slot> <slot></slot>
</ul> </ul>
@ -70,21 +70,17 @@ defineExpose({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.meijqfqm { .root {
position: relative; position: relative;
overflow: clip; overflow: clip;
display: grid; display: grid;
place-items: center; place-items: center;
}
> .canvas { .tags {
display: block; position: absolute;
} top: 999px;
left: 999px;
> .tags {
position: absolute;
top: 999px;
left: 999px;
}
} }
</style> </style>

View File

@ -46,12 +46,6 @@ const onUserRemoved = () => {
tlComponent.pagingComponent?.reload(); tlComponent.pagingComponent?.reload();
}; };
const onChangeFollowing = () => {
if (!tlComponent.pagingComponent?.backed) {
tlComponent.pagingComponent?.reload();
}
};
let endpoint; let endpoint;
let query; let query;
let connection; let connection;
@ -79,8 +73,6 @@ if (props.src === 'antenna') {
connection.on('note', prepend); connection.on('note', prepend);
connection2 = stream.useChannel('main'); connection2 = stream.useChannel('main');
connection2.on('follow', onChangeFollowing);
connection2.on('unfollow', onChangeFollowing);
} else if (props.src === 'local') { } else if (props.src === 'local') {
endpoint = 'notes/local-timeline'; endpoint = 'notes/local-timeline';
query = { query = {

View File

@ -1,10 +1,10 @@
<template> <template>
<div class="adhpbeou"> <div>
<div class="label" @click="focus"><slot name="label"></slot></div> <div :class="$style.label" @click="focus"><slot name="label"></slot></div>
<div class="content"> <div :class="$style.content">
<slot></slot> <slot></slot>
</div> </div>
<div class="caption"><slot name="caption"></slot></div> <div :class="$style.caption"><slot name="caption"></slot></div>
</div> </div>
</template> </template>
@ -16,26 +16,24 @@ function focus() {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.adhpbeou { .label {
> .label { font-size: 0.85em;
font-size: 0.85em; padding: 0 0 8px 0;
padding: 0 0 8px 0; user-select: none;
user-select: none;
&:empty { &:empty {
display: none; display: none;
}
} }
}
> .caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--fgTransparentWeak); color: var(--fgTransparentWeak);
&:empty { &:empty {
display: none; display: none;
}
} }
} }
</style> </style>

View File

@ -1,8 +1,9 @@
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import type { Locale } from '../../../locales';
import { locale } from '@/config'; import { locale } from '@/config';
import { I18n } from '@/scripts/i18n'; import { I18n } from '@/scripts/i18n';
export const i18n = markRaw(new I18n(locale)); export const i18n = markRaw(new I18n<Locale>(locale));
export function updateI18n(newLocale) { export function updateI18n(newLocale) {
i18n.ts = newLocale; i18n.ts = newLocale;

View File

@ -2,22 +2,28 @@
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="700"> <MkSpacer :contentMax="700">
<div v-if="tab === 'featured'" class="rknalgpo"> <div v-if="tab === 'featured'">
<MkPagination v-slot="{items}" :pagination="featuredPagesPagination"> <MkPagination v-slot="{items}" :pagination="featuredPagesPagination">
<MkPagePreview v-for="page in items" :key="page.id" class="ckltabjg" :page="page"/> <div class="_gaps">
<MkPagePreview v-for="page in items" :key="page.id" :page="page"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
<div v-else-if="tab === 'my'" class="rknalgpo my"> <div v-else-if="tab === 'my'" class="_gaps">
<MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
<MkPagination v-slot="{items}" :pagination="myPagesPagination"> <MkPagination v-slot="{items}" :pagination="myPagesPagination">
<MkPagePreview v-for="page in items" :key="page.id" class="ckltabjg" :page="page"/> <div class="_gaps">
<MkPagePreview v-for="page in items" :key="page.id" :page="page"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
<div v-else-if="tab === 'liked'" class="rknalgpo"> <div v-else-if="tab === 'liked'">
<MkPagination v-slot="{items}" :pagination="likedPagesPagination"> <MkPagination v-slot="{items}" :pagination="likedPagesPagination">
<MkPagePreview v-for="like in items" :key="like.page.id" class="ckltabjg" :page="like.page"/> <div class="_gaps">
<MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
</MkSpacer> </MkSpacer>
@ -79,21 +85,3 @@ definePageMetadata(computed(() => ({
icon: 'ti ti-note', icon: 'ti ti-note',
}))); })));
</script> </script>
<style lang="scss" scoped>
.rknalgpo {
&.my .ckltabjg:first-child {
margin-top: 16px;
}
.ckltabjg:not(:last-child) {
margin-bottom: 8px;
}
@media (min-width: 500px) {
.ckltabjg:not(:last-child) {
margin-bottom: 16px;
}
}
}
</style>

View File

@ -9,11 +9,11 @@
</template> </template>
<template #default="{items}"> <template #default="{items}">
<div class="_gaps"> <div class="_gaps">
<div v-for="token in items" :key="token.id" class="_panel bfomjevm"> <div v-for="token in items" :key="token.id" class="_panel" :class="$style.app">
<img v-if="token.iconUrl" class="icon" :src="token.iconUrl" alt=""/> <img v-if="token.iconUrl" :class="$style.appIcon" :src="token.iconUrl" alt=""/>
<div class="body"> <div :class="$style.appBody">
<div class="name">{{ token.name }}</div> <div :class="$style.appName">{{ token.name }}</div>
<div class="description">{{ token.description }}</div> <div>{{ token.description }}</div>
<MkKeyValue oneline> <MkKeyValue oneline>
<template #key>{{ i18n.ts.installedDate }}</template> <template #key>{{ i18n.ts.installedDate }}</template>
<template #value><MkTime :time="token.createdAt"/></template> <template #value><MkTime :time="token.createdAt"/></template>
@ -28,7 +28,7 @@
<li v-for="p in token.permission" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li> <li v-for="p in token.permission" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li>
</ul> </ul>
</details> </details>
<div class="actions"> <div>
<MkButton inline danger @click="revoke(token)"><i class="ti ti-trash"></i></MkButton> <MkButton inline danger @click="revoke(token)"><i class="ti ti-trash"></i></MkButton>
</div> </div>
</div> </div>
@ -75,27 +75,27 @@ definePageMetadata({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.bfomjevm { .app {
display: flex; display: flex;
padding: 16px; padding: 16px;
}
> .icon { .appIcon {
display: block; display: block;
flex-shrink: 0; flex-shrink: 0;
margin: 0 12px 0 0; margin: 0 12px 0 0;
width: 50px; width: 50px;
height: 50px; height: 50px;
border-radius: 8px; border-radius: 8px;
} }
> .body { .appBody {
width: calc(100% - 62px); width: calc(100% - 62px);
position: relative; position: relative;
}
> .name { .appName {
font-weight: bold; font-weight: bold;
}
}
} }
</style> </style>

View File

@ -1,37 +1,83 @@
<template> <template>
<div> <div :class="$style.root">
{{ i18n.ts.processing }} <MkAnimBg style="position: fixed; top: 0;"/>
<div :class="$style.formContainer">
<form :class="$style.form" class="_panel" @submit.prevent="submit()">
<div :class="$style.banner">
<i class="ti ti-user-check"></i>
</div>
<div class="_gaps_m" style="padding: 32px;">
<div>{{ i18n.t('clickToFinishEmailVerification', { ok: i18n.ts.gotIt }) }}</div>
<div>
<MkButton gradate large rounded type="submit" :disabled="submitting" data-cy-admin-ok style="margin: 0 auto;">
{{ submitting ? i18n.ts.processing : i18n.ts.gotIt }}<MkEllipsis v-if="submitting"/>
</MkButton>
</div>
</div>
</form>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { } from 'vue';
import * as os from '@/os'; import MkButton from '@/components/MkButton.vue';
import MkAnimBg from '@/components/MkAnimBg.vue';
import { login } from '@/account'; import { login } from '@/account';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import * as os from '@/os';
let submitting = $ref(false);
const props = defineProps<{ const props = defineProps<{
code: string; code: string;
}>(); }>();
onMounted(async () => { function submit() {
await os.alert({ if (submitting) return;
type: 'info', submitting = true;
text: i18n.t('clickToFinishEmailVerification', { ok: i18n.ts.gotIt }),
}); os.api('signup-pending', {
const res = await os.apiWithDialog('signup-pending', {
code: props.code, code: props.code,
}).then(res => {
return login(res.i, '/');
}).catch(() => {
submitting = false;
os.alert({
type: 'error',
text: i18n.ts.somethingHappened,
});
}); });
login(res.i, '/'); }
});
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata({
title: i18n.ts.signup,
icon: 'ti ti-user',
});
</script> </script>
<style lang="scss" module>
.root {
}
.formContainer {
min-height: 100svh;
padding: 32px 32px 64px 32px;
box-sizing: border-box;
display: grid;
place-content: center;
}
.form {
position: relative;
z-index: 10;
border-radius: var(--radius);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: clip;
max-width: 500px;
}
.banner {
padding: 16px;
text-align: center;
font-size: 26px;
background-color: var(--accentedBg);
color: var(--accent);
}
</style>

View File

@ -34,7 +34,3 @@ const props = defineProps<{
}>(); }>();
</script> </script>
<style lang="scss" scoped>
</style>

View File

@ -56,6 +56,3 @@ definePageMetadata(computed(() => user ? {
avatar: user, avatar: user,
} : null)); } : null));
</script> </script>
<style lang="scss" scoped>
</style>

View File

@ -56,6 +56,3 @@ definePageMetadata(computed(() => user ? {
avatar: user, avatar: user,
} : null)); } : null));
</script> </script>
<style lang="scss" scoped>
</style>

View File

@ -2,21 +2,19 @@
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<div> <div>
<Transition name="fade" mode="out-in"> <div v-if="user">
<div v-if="user"> <XHome v-if="tab === 'home'" :user="user"/>
<XHome v-if="tab === 'home'" :user="user"/> <XTimeline v-else-if="tab === 'notes'" :user="user"/>
<XTimeline v-else-if="tab === 'notes'" :user="user"/> <XActivity v-else-if="tab === 'activity'" :user="user"/>
<XActivity v-else-if="tab === 'activity'" :user="user"/> <XAchievements v-else-if="tab === 'achievements'" :user="user"/>
<XAchievements v-else-if="tab === 'achievements'" :user="user"/> <XReactions v-else-if="tab === 'reactions'" :user="user"/>
<XReactions v-else-if="tab === 'reactions'" :user="user"/> <XClips v-else-if="tab === 'clips'" :user="user"/>
<XClips v-else-if="tab === 'clips'" :user="user"/> <XLists v-else-if="tab === 'lists'" :user="user"/>
<XLists v-else-if="tab === 'lists'" :user="user"/> <XPages v-else-if="tab === 'pages'" :user="user"/>
<XPages v-else-if="tab === 'pages'" :user="user"/> <XGallery v-else-if="tab === 'gallery'" :user="user"/>
<XGallery v-else-if="tab === 'gallery'" :user="user"/> </div>
</div> <MkError v-else-if="error" @retry="fetchUser()"/>
<MkError v-else-if="error" @retry="fetchUser()"/> <MkLoading v-else/>
<MkLoading v-else/>
</Transition>
</div> </div>
</MkStickyContainer> </MkStickyContainer>
</template> </template>
@ -118,14 +116,3 @@ definePageMetadata(computed(() => user ? {
}, },
} : null)); } : null));
</script> </script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.125s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -1,9 +1,9 @@
<template> <template>
<MkContainer :naked="widgetProps.transparent" :showHeader="false" data-cy-mkw-clock class="mkw-clock"> <MkContainer :naked="widgetProps.transparent" :showHeader="false" data-cy-mkw-clock>
<div class="vubelbmv" :class="widgetProps.size"> <div :class="[$style.root, $style[widgetProps.size]]">
<div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace label a abbrev">{{ tzAbbrev }}</div> <div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace" :class="[$style.label, $style.a]">{{ tzAbbrev }}</div>
<MkAnalogClock <MkAnalogClock
class="clock" :class="$style.clock"
:thickness="widgetProps.thickness" :thickness="widgetProps.thickness"
:offset="tzOffset" :offset="tzOffset"
:graduations="widgetProps.graduations" :graduations="widgetProps.graduations"
@ -11,8 +11,8 @@
:twentyfour="widgetProps.twentyFour" :twentyfour="widgetProps.twentyFour"
:sAnimation="widgetProps.sAnimation" :sAnimation="widgetProps.sAnimation"
/> />
<MkDigitalClock v-if="widgetProps.label === 'time' || widgetProps.label === 'timeAndTz'" class="_monospace label c time" :showS="false" :offset="tzOffset"/> <MkDigitalClock v-if="widgetProps.label === 'time' || widgetProps.label === 'timeAndTz'" :class="[$style.label, $style.c]" class="_monospace" :showS="false" :offset="tzOffset"/>
<div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace label d offset">{{ tzOffsetLabel }}</div> <div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace" :class="[$style.label, $style.d]">{{ tzOffsetLabel }}</div>
</div> </div>
</MkContainer> </MkContainer>
</template> </template>
@ -140,39 +140,10 @@ defineExpose<WidgetComponentExpose>({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.vubelbmv { .root {
position: relative; position: relative;
> .label {
position: absolute;
opacity: 0.7;
&.a {
top: 14px;
left: 14px;
}
&.b {
top: 14px;
right: 14px;
}
&.c {
bottom: 14px;
left: 14px;
}
&.d {
bottom: 14px;
right: 14px;
}
}
> .clock {
margin: auto;
}
&.small { &.small {
padding: 12px; padding: 12px;
@ -197,4 +168,33 @@ defineExpose<WidgetComponentExpose>({
} }
} }
} }
.label {
position: absolute;
opacity: 0.7;
&.a {
top: 14px;
left: 14px;
}
&.b {
top: 14px;
right: 14px;
}
&.c {
bottom: 14px;
left: 14px;
}
&.d {
bottom: 14px;
right: 14px;
}
}
.clock {
margin: auto;
}
</style> </style>

View File

@ -72,7 +72,3 @@ defineExpose<WidgetComponentExpose>({
id: props.widget ? props.widget.id : null, id: props.widget ? props.widget.id : null,
}); });
</script> </script>
<style lang="scss" scoped>
</style>

View File

@ -1,8 +1,10 @@
<template> <template>
<div data-cy-mkw-onlineUsers class="mkw-onlineUsers" :class="{ _panel: !widgetProps.transparent, pad: !widgetProps.transparent }"> <div data-cy-mkw-onlineUsers :class="[$style.root, { _panel: !widgetProps.transparent, [$style.pad]: !widgetProps.transparent }]">
<I18n v-if="onlineUsersCount" :src="i18n.ts.onlineUsersCount" textTag="span" class="text"> <span :class="$style.text">
<template #n><b>{{ number(onlineUsersCount) }}</b></template> <I18n v-if="onlineUsersCount" :src="i18n.ts.onlineUsersCount" textTag="span">
</I18n> <template #n><b style="color: #41b781;">{{ number(onlineUsersCount) }}</b></template>
</I18n>
</span>
</div> </div>
</template> </template>
@ -55,22 +57,16 @@ defineExpose<WidgetComponentExpose>({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.mkw-onlineUsers { .root {
text-align: center; text-align: center;
&.pad { &.pad {
padding: 16px 0; padding: 16px 0;
} }
}
> .text { .text {
::v-deep(b) { color: var(--fgTransparentWeak);
color: #41b781;
}
::v-deep(span) {
opacity: 0.7;
}
}
} }
</style> </style>

View File

@ -1,11 +1,12 @@
<template> <template>
<svg class="hsalcinq" viewBox="0 0 1 1" preserveAspectRatio="none"> <svg :class="$style.root" viewBox="0 0 1 1" preserveAspectRatio="none">
<circle <circle
:r="r" :r="r"
cx="50%" cy="50%" cx="50%" cy="50%"
fill="none" fill="none"
stroke-width="0.1" stroke-width="0.1"
stroke="rgba(0, 0, 0, 0.05)" stroke="rgba(0, 0, 0, 0.05)"
:class="$style.circle"
/> />
<circle <circle
:r="r" :r="r"
@ -16,7 +17,7 @@
stroke-width="0.1" stroke-width="0.1"
:stroke="color" :stroke="color"
/> />
<text x="50%" y="50%" dy="0.05" text-anchor="middle">{{ (value * 100).toFixed(0) }}%</text> <text x="50%" y="50%" dy="0.05" text-anchor="middle" :class="$style.text">{{ (value * 100).toFixed(0) }}%</text>
</svg> </svg>
</template> </template>
@ -33,20 +34,20 @@ const color = $computed(() => `hsl(${180 - (props.value * 180)}, 80%, 70%)`);
const strokeDashoffset = $computed(() => (1 - props.value) * (Math.PI * (r * 2))); const strokeDashoffset = $computed(() => (1 - props.value) * (Math.PI * (r * 2)));
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.hsalcinq { .root {
display: block; display: block;
height: 100%; height: 100%;
}
> circle { .circle {
transform-origin: center; transform-origin: center;
transform: rotate(-90deg); transform: rotate(-90deg);
transition: stroke-dashoffset 0.5s ease; transition: stroke-dashoffset 0.5s ease;
} }
> text { .text {
font-size: 0.15px; font-size: 0.15px;
fill: currentColor; fill: currentColor;
}
} }
</style> </style>

View File

@ -6,6 +6,7 @@ import { type UserConfig, defineConfig } from 'vite';
import ReactivityTransform from '@vue-macros/reactivity-transform/vite'; import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
import locales from '../../locales'; import locales from '../../locales';
import generateDTS from '../../locales/generateDTS';
import meta from '../../package.json'; import meta from '../../package.json';
import pluginJson5 from './vite.json5'; import pluginJson5 from './vite.json5';
@ -64,6 +65,10 @@ export function getConfig(): UserConfig {
}), }),
] ]
: [], : [],
{
name: 'locale:generateDTS',
buildStart: generateDTS,
},
], ],
resolve: { resolve: {

File diff suppressed because it is too large Load Diff