Compare commits

..

No commits in common. "2c6fa13a2cc0bb551e81e39ea3d706f1013520ba" and "591b35604e980b31c73ebfb4ee03c6046940b25f" have entirely different histories.

14 changed files with 26 additions and 84 deletions

View File

@ -15,14 +15,10 @@
## 2023.x.x (unreleased)
### General
- Feat: プラグインのスクリプトとデータを端末間で同期をできるようになりました
- 既存のプラグインはローカルに保存されています。
- ローカルのプラグインはプラグインの管理にあるボタンからアカウントに移動できます。
- `Mk:load`と`Mk:save`に引数`toAccount?: bool`が追加され、`true`にするとアカウントでデータを共有できます。(デフォルトは`false`)
-
### Client
- Fix: アイコンデコレーションが複数の場所で見切れている問題を修正
― Fix: 「フォロー中の人全員の返信を含める/含めないようにする」のボタンを押下した際の確認が機能していない問題を修正
-
### Server
- Fix: トークンのないプラグインをアンインストールするときにエラーが出ないように

3
locales/index.d.ts vendored
View File

@ -1167,9 +1167,6 @@ export interface Locale {
"overrideSourceCodeOnly": string;
"syncSetting": string;
"syncing": string;
"movePluginToAccount": string;
"movePluginToAccountConfirm": string;
"overridePluginConfirm": string;
"_announcement": {
"forExistingUsers": string;
"forExistingUsersDescription": string;

View File

@ -1164,9 +1164,6 @@ duplicateSyncedPlugin: "このプラグインは同期されているプラグ
overrideSourceCodeOnly: "コードのみを上書きする"
syncSetting: "同期設定"
syncing: "他端末と同期"
movePluginToAccount: "アカウントに移行"
movePluginToAccountConfirm: "プラグインをアカウントに移行して他端末と同期しますか?プラグインとプラグイン設定以外は引き継がれません。"
overridePluginConfirm: "すでにプラグインがアカウントに存在します。上書きしますか?アカウントにあるプラグインデータは削除されます。"
_announcement:
forExistingUsers: "既存ユーザーのみ"

View File

@ -47,7 +47,6 @@ export class InstanceEntityService {
faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor,
infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
};
}

View File

@ -103,10 +103,5 @@ export const packedFederationInstanceSchema = {
optional: false, nullable: true,
format: 'date-time',
},
latestRequestReceivedAt: {
type: 'string',
optional: false, nullable: true,
format: 'date-time',
},
},
} as const;

View File

@ -58,7 +58,7 @@ export async function mainBoot() {
}
});
const plugins = ColdDeviceStorage.get('plugins').filter(p => p.active);
let plugins = ColdDeviceStorage.get('plugins').filter(p => p.active);
const accountPlugins = Object.values(await getPluginList()).filter(p => p.active);
plugins.push(...accountPlugins);
@ -282,7 +282,7 @@ export async function mainBoot() {
// プラグインに変更が入ったら自動でリロードする
stream.useChannel('main').on('registryUpdated', ({ scope, key }: { scope: string[], key: string, value: any }) => {
if (scope.length === 1 && scope[0] === 'client' && key === 'plugins') {
if (scope[0] === 'client' && key === 'plugins') {
unisonReload();
}
});

View File

@ -1072,7 +1072,6 @@ defineExpose({
.preview {
padding: 16px 20px 0 20px;
min-height: 75px;
max-height: 150px;
overflow: auto;
}

View File

@ -73,6 +73,7 @@ function getReactionName(reaction: string): string {
}
.users {
contain: content;
flex: 1;
min-width: 0;
margin: -4px 14px 0 10px;
@ -84,7 +85,7 @@ function getReactionName(reaction: string): string {
line-height: 24px;
padding-top: 4px;
white-space: nowrap;
overflow: visible;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -148,13 +148,12 @@ async function reloadAsk() {
}
async function updateRepliesAll(withReplies: boolean) {
const { canceled } = await os.confirm({
const { canceled } = os.confirm({
type: 'warning',
text: withReplies ? i18n.ts.confirmShowRepliesAll : i18n.ts.confirmHideRepliesAll,
});
if (canceled) return;
os.api('following/update-all', { withReplies });
await os.api('following/update-all', { withReplies });
}
watch([

View File

@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_buttons">
<MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="ti ti-settings"></i> {{ i18n.ts.settings }}</MkButton>
<MkButton v-if="!plugin.fromAccount" inline @click="moveToAccount(plugin)"><i class="ti ti-link"></i> {{ i18n.ts.movePluginToAccount }}</MkButton>
<MkButton inline danger @click="uninstall(plugin)"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton>
</div>
@ -76,8 +75,6 @@ import { unisonReload } from '@/scripts/unison-reload.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { getPluginList } from '@/plugin.js';
import { toHash } from '@/scripts/xxhash.js';
import { savePluginToAccount } from '@/scripts/install-plugin.js';
let plugins = Object.values(await getPluginList());
plugins.push(...ColdDeviceStorage.get('plugins'));
@ -90,8 +87,7 @@ async function uninstall(plugin) {
await os.api('i/registry/remove-all-keys-in-scope', { scope: ['client', 'aiscript', 'plugins', plugin.id] });
await os.api('i/registry/set', { scope: ['client'], key: 'plugins', value: plugins });
} else {
const coldPlugins = ColdDeviceStorage.get('plugins');
ColdDeviceStorage.set('plugins', coldPlugins.filter(x => x.id !== plugin.id));
ColdDeviceStorage.set('plugins', plugins.value.filter(x => x.id !== plugin.id));
}
await os.apiWithDialog('i/revoke-token', {
token: plugin.token,
@ -101,37 +97,6 @@ async function uninstall(plugin) {
});
}
async function moveToAccount(plugin) {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.ts.movePluginToAccountConfirm,
});
if (canceled) return;
const hash = await toHash(plugin.name, plugin.author);
const plugins = await getPluginList();
if (Object.keys(plugins).some(v => v === hash)) {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.ts.overridePluginConfirm,
});
if (canceled) return;
await os.api('i/registry/remove-all-keys-in-scope', { scope: ['client', 'aiscript', 'plugins', hash] });
}
const coldPlugins = ColdDeviceStorage.get('plugins');
ColdDeviceStorage.set('plugins', coldPlugins.filter(x => x.id !== plugin.id));
plugin.id = hash;
plugin.fromAccount = true;
plugins[hash] = plugin;
await os.api('i/registry/set', { scope: ['client'], key: 'plugins', value: plugins });
}
function copy(plugin) {
copyToClipboard(plugin.src ?? '');
os.success();

View File

@ -14,11 +14,7 @@ export async function loadScriptStorage(toAccount: boolean, scriptData: ScriptDa
value = await api('i/registry/get', { scope: ['client', 'aiscript', scriptData.type, scriptData.id!], key: key });
}
} else {
if (scriptData.type === 'widget') {
value = miLocalStorage.getItem(`aiscript:${scriptData.type}:${key}`);
} else {
value = miLocalStorage.getItem(`aiscript:${scriptData.type}:${scriptData.id!}:${key}`);
}
value = miLocalStorage.getItem(`aiscript:${scriptData.type}:${key}`);
}
if (value === null) return null;
@ -34,10 +30,6 @@ export async function saveScriptStorage(toAccount: boolean, scriptData: ScriptDa
await api('i/registry/set', { scope: ['client', 'aiscript', scriptData.type, scriptData.id!], key: key, value: jsonValue });
}
} else {
if (scriptData.type === 'widget') {
miLocalStorage.setItem(`aiscript:${scriptData.type}:${key}`, jsonValue);
} else {
miLocalStorage.setItem(`aiscript:${scriptData.type}:${scriptData.id!}:${key}`, jsonValue);
}
miLocalStorage.setItem(`aiscript:${scriptData.type}:${key}`, jsonValue);
}
}

View File

@ -6,13 +6,13 @@
import { defineAsyncComponent } from 'vue';
import { compareVersions } from 'compare-versions';
import { v4 as uuid } from 'uuid';
import xxhash from 'xxhash-wasm';
import { Interpreter, Parser, utils } from '@syuilo/aiscript';
import type { Plugin } from '@/store.js';
import { ColdDeviceStorage } from '@/store.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { getPluginList } from '@/plugin.js';
import { toHash } from './xxhash.js';
export type AiScriptPluginMeta = {
name: string;
@ -25,6 +25,11 @@ export type AiScriptPluginMeta = {
const parser = new Parser();
async function toHash(name: string, author: string) {
const { h32ToString } = await xxhash();
return h32ToString(author + name);
}
export function savePlugin({ id, meta, src, token }: {
id: string;
meta: AiScriptPluginMeta;
@ -48,7 +53,7 @@ async function savePluginToAccount(pluginOnlyOverride: boolean, { hash, meta, sr
src: string;
token: string;
}) {
const plugins = await getPluginList();
let plugins = await getPluginList();
// pluginOnlyOverrideがtrueになっているということはすでに重複していることが確定している
const configData = pluginOnlyOverride ? plugins[hash].configData : {};
const pluginToken = pluginOnlyOverride ? plugins[hash].token : token;
@ -164,13 +169,15 @@ export async function installPlugin(code: string, meta?: AiScriptPluginMeta) {
});
if (isLocal) {
savePlugin({
id: uuid(),
meta: realMeta,
token,
src: code,
});
} else {
}
else {
await savePluginToAccount(pluginOnlyOverride, {
hash: pluginHash,
meta: realMeta,

View File

@ -1,6 +0,0 @@
import xxhash from 'xxhash-wasm';
export async function toHash(name: string, author: string) {
const { h32ToString } = await xxhash();
return h32ToString(author + name);
}

View File

@ -176,7 +176,7 @@ function more(ev: MouseEvent) {
.bottom {
position: sticky;
bottom: 0;
padding-top: 20px;
padding: 20px 0;
background: var(--X14);
-webkit-backdrop-filter: var(--blur, blur(8px));
backdrop-filter: var(--blur, blur(8px));
@ -228,10 +228,11 @@ function more(ev: MouseEvent) {
position: relative;
display: flex;
align-items: center;
padding: 20px 0 20px 30px;
padding-left: 30px;
width: 100%;
text-align: left;
box-sizing: border-box;
margin-top: 16px;
overflow: clip;
}
@ -362,7 +363,7 @@ function more(ev: MouseEvent) {
.bottom {
position: sticky;
bottom: 0;
padding-top: 20px;
padding: 20px 0;
background: var(--X14);
-webkit-backdrop-filter: var(--blur, blur(8px));
backdrop-filter: var(--blur, blur(8px));
@ -373,6 +374,7 @@ function more(ev: MouseEvent) {
position: relative;
width: 100%;
height: 52px;
margin-bottom: 16px;
text-align: center;
&:before {
@ -409,7 +411,6 @@ function more(ev: MouseEvent) {
.account {
display: block;
text-align: center;
padding: 20px 0;
width: 100%;
overflow: clip;
}