Merge branch 'develop' into refactor-mkselect
This commit is contained in:
commit
0f3500a82e
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,10 +1,16 @@
|
||||||
## Unreleased
|
## 2025.9.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
-
|
-
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
-
|
- Enhance: AiScriptAppウィジェットで構文エラーを検知してもダイアログではなくウィジェット内にエラーを表示するように
|
||||||
|
- Enhance: /flushページでサイトキャッシュをクリアできるようになりました
|
||||||
|
- Enhance: クリップ/リスト/アンテナ/ロール追加系メニュー項目において、表示件数を拡張
|
||||||
|
- Fix: プッシュ通知を有効にできない問題を修正
|
||||||
|
- Fix: RSSティッカーウィジェットが正しく動作しない問題を修正
|
||||||
|
- Fix: プロファイルを復元後アカウントの切り替えができない問題を修正
|
||||||
|
- Fix: エラー画像が横に引き伸ばされてしまう問題に対応
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
-
|
-
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2025.8.0",
|
"version": "2025.9.0-alpha.1",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -201,6 +201,8 @@ export class ClientServerService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
|
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
|
||||||
|
const configUrl = new URL(this.config.url);
|
||||||
|
|
||||||
fastify.register(fastifyView, {
|
fastify.register(fastifyView, {
|
||||||
root: _dirname + '/views',
|
root: _dirname + '/views',
|
||||||
engine: {
|
engine: {
|
||||||
|
@ -239,7 +241,6 @@ export class ClientServerService {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const configUrl = new URL(this.config.url);
|
|
||||||
const urlOriginWithoutPort = configUrl.origin.replace(/:\d+$/, '');
|
const urlOriginWithoutPort = configUrl.origin.replace(/:\d+$/, '');
|
||||||
|
|
||||||
const port = (process.env.VITE_PORT ?? '5173');
|
const port = (process.env.VITE_PORT ?? '5173');
|
||||||
|
@ -887,6 +888,22 @@ export class ClientServerService {
|
||||||
[, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/');
|
[, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/');
|
||||||
|
|
||||||
fastify.get('/flush', async (request, reply) => {
|
fastify.get('/flush', async (request, reply) => {
|
||||||
|
let sendHeader = true;
|
||||||
|
|
||||||
|
if (request.headers['origin']) {
|
||||||
|
const originURL = new URL(request.headers['origin']);
|
||||||
|
if (originURL.protocol !== 'https:') { // Clear-Site-Data only supports https
|
||||||
|
sendHeader = false;
|
||||||
|
}
|
||||||
|
if (originURL.host !== configUrl.host) {
|
||||||
|
sendHeader = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendHeader) {
|
||||||
|
reply.header('Clear-Site-Data', '"*"');
|
||||||
|
}
|
||||||
|
reply.header('Set-Cookie', 'http-flush-failed=1; Path=/flush; Max-Age=60');
|
||||||
return await reply.view('flush');
|
return await reply.view('flush');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,41 +6,45 @@ html
|
||||||
const msg = document.getElementById('msg');
|
const msg = document.getElementById('msg');
|
||||||
const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`;
|
const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`;
|
||||||
|
|
||||||
message('Start flushing.');
|
if (!document.cookie) {
|
||||||
|
message('Your site data is fully cleared by your browser.');
|
||||||
|
message(successText);
|
||||||
|
} else {
|
||||||
|
message('Your browser does not support Clear-Site-Data header. Start opportunistic flushing.');
|
||||||
|
(async function() {
|
||||||
|
try {
|
||||||
|
localStorage.clear();
|
||||||
|
message('localStorage cleared.');
|
||||||
|
|
||||||
(async function() {
|
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => {
|
||||||
try {
|
const delidb = indexedDB.deleteDatabase(name);
|
||||||
localStorage.clear();
|
delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`));
|
||||||
message('localStorage cleared.');
|
delidb.onerror = e => rej(e)
|
||||||
|
}));
|
||||||
|
|
||||||
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => {
|
await Promise.all(idbPromises);
|
||||||
const delidb = indexedDB.deleteDatabase(name);
|
|
||||||
delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`));
|
|
||||||
delidb.onerror = e => rej(e)
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Promise.all(idbPromises);
|
if (navigator.serviceWorker.controller) {
|
||||||
|
navigator.serviceWorker.controller.postMessage('clear');
|
||||||
|
await navigator.serviceWorker.getRegistrations()
|
||||||
|
.then(registrations => {
|
||||||
|
return Promise.all(registrations.map(registration => registration.unregister()));
|
||||||
|
})
|
||||||
|
.catch(e => { throw new Error(e) });
|
||||||
|
}
|
||||||
|
|
||||||
if (navigator.serviceWorker.controller) {
|
message(successText);
|
||||||
navigator.serviceWorker.controller.postMessage('clear');
|
} catch (e) {
|
||||||
await navigator.serviceWorker.getRegistrations()
|
message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`);
|
||||||
.then(registrations => {
|
message(`\nIf you retry more than 3 times, try manually clearing the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを手動で消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`)
|
||||||
return Promise.all(registrations.map(registration => registration.unregister()));
|
|
||||||
})
|
console.error(e);
|
||||||
.catch(e => { throw new Error(e) });
|
setTimeout(() => {
|
||||||
|
location = '/';
|
||||||
|
}, 10000)
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
message(successText);
|
}
|
||||||
} catch (e) {
|
|
||||||
message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`);
|
|
||||||
message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`)
|
|
||||||
|
|
||||||
console.error(e);
|
|
||||||
setTimeout(() => {
|
|
||||||
location = '/';
|
|
||||||
}, 10000)
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
function message(text) {
|
function message(text) {
|
||||||
msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`)
|
msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`)
|
||||||
|
|
|
@ -251,13 +251,30 @@ export async function openAccountMenu(opts: {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else { // プロファイルを復元した場合などはアカウントのトークンや詳細情報はstoreにキャッシュされていない
|
||||||
return {
|
return {
|
||||||
type: 'button' as const,
|
type: 'button' as const,
|
||||||
text: username,
|
text: username,
|
||||||
active: opts.active != null ? opts.active === id : false,
|
active: opts.active != null ? opts.active === id : false,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
// TODO
|
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {
|
||||||
|
initialUsername: username,
|
||||||
|
}, {
|
||||||
|
done: async (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
|
||||||
|
store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + res.id]: res.i });
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
fetchAccount(res.i, id).then(account => {
|
||||||
|
callback(account);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
switchAccount(host, id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closed: () => {
|
||||||
|
dispose();
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import * as Misskey from 'misskey-js';
|
||||||
import { Cache } from '@/utility/cache.js';
|
import { Cache } from '@/utility/cache.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
|
||||||
export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => misskeyApi('clips/list'));
|
export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => misskeyApi('clips/list', { limit: 30 }));
|
||||||
export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list'));
|
export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list', { limit: 30 }));
|
||||||
export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list'));
|
export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list'));
|
||||||
export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list'));
|
export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list', { limit: 30 }));
|
||||||
export const favoritedChannelsCache = new Cache<Misskey.entities.Channel[]>(1000 * 60 * 30, () => misskeyApi('channels/my-favorites', { limit: 100 }));
|
export const favoritedChannelsCache = new Cache<Misskey.entities.Channel[]>(1000 * 60 * 30, () => misskeyApi('channels/my-favorites', { limit: 100 }));
|
||||||
|
|
|
@ -78,7 +78,7 @@ function subscribe() {
|
||||||
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
|
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
|
||||||
return promiseDialog(registration.value.pushManager.subscribe({
|
return promiseDialog(registration.value.pushManager.subscribe({
|
||||||
userVisibleOnly: true,
|
userVisibleOnly: true,
|
||||||
applicationServerKey: urlBase64ToBase64(instance.swPublickey),
|
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
|
||||||
})
|
})
|
||||||
.then(async subscription => {
|
.then(async subscription => {
|
||||||
pushSubscription.value = subscription;
|
pushSubscription.value = subscription;
|
||||||
|
@ -131,16 +131,22 @@ function encode(buffer: ArrayBuffer | null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the URL safe base64 string to a base64 string
|
* Convert the URL safe base64 string to a Uint8Array
|
||||||
* @param base64String base64 string
|
* @param base64String base64 string
|
||||||
*/
|
*/
|
||||||
function urlBase64ToBase64(base64String: string): string {
|
function urlBase64ToUint8Array(base64String: string): Uint8Array {
|
||||||
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
||||||
const base64 = (base64String + padding)
|
const base64 = (base64String + padding)
|
||||||
.replace(/-/g, '+')
|
.replace(/-/g, '+')
|
||||||
.replace(/_/g, '/');
|
.replace(/_/g, '/');
|
||||||
|
|
||||||
return base64;
|
const rawData = window.atob(base64);
|
||||||
|
const outputArray = new Uint8Array(rawData.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < rawData.length; ++i) {
|
||||||
|
outputArray[i] = rawData.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return outputArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (navigator.serviceWorker == null) {
|
if (navigator.serviceWorker == null) {
|
||||||
|
|
|
@ -69,9 +69,11 @@ import MkInfo from '@/components/MkInfo.vue';
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
message?: string,
|
message?: string,
|
||||||
openOnRemote?: OpenOnRemoteOptions,
|
openOnRemote?: OpenOnRemoteOptions,
|
||||||
|
initialUsername?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
message: '',
|
message: '',
|
||||||
openOnRemote: undefined,
|
openOnRemote: undefined,
|
||||||
|
initialUsername: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -81,7 +83,7 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const host = toUnicode(configHost);
|
const host = toUnicode(configHost);
|
||||||
|
|
||||||
const username = ref('');
|
const username = ref(props.initialUsername ?? '');
|
||||||
|
|
||||||
//#region Open on remote
|
//#region Open on remote
|
||||||
function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
|
function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
|
||||||
|
|
|
@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
key="input"
|
key="input"
|
||||||
:message="message"
|
:message="message"
|
||||||
:openOnRemote="openOnRemote"
|
:openOnRemote="openOnRemote"
|
||||||
|
:initialUsername="initialUsername"
|
||||||
|
|
||||||
@usernameSubmitted="onUsernameSubmitted"
|
@usernameSubmitted="onUsernameSubmitted"
|
||||||
@passkeyClick="onPasskeyLogin"
|
@passkeyClick="onPasskeyLogin"
|
||||||
|
@ -89,10 +90,12 @@ const props = withDefaults(defineProps<{
|
||||||
autoSet?: boolean;
|
autoSet?: boolean;
|
||||||
message?: string,
|
message?: string,
|
||||||
openOnRemote?: OpenOnRemoteOptions,
|
openOnRemote?: OpenOnRemoteOptions,
|
||||||
|
initialUsername?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
autoSet: false,
|
autoSet: false,
|
||||||
message: '',
|
message: '',
|
||||||
openOnRemote: undefined,
|
openOnRemote: undefined,
|
||||||
|
initialUsername: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const page = ref<'input' | 'password' | 'totp' | 'passkey'>('input');
|
const page = ref<'input' | 'password' | 'totp' | 'passkey'>('input');
|
||||||
|
|
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<button :class="$style.closeButton" class="_button" @click="onClose"><i class="ti ti-x"></i></button>
|
<button :class="$style.closeButton" class="_button" @click="onClose"><i class="ti ti-x"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.content">
|
<div :class="$style.content">
|
||||||
<MkSignin :autoSet="autoSet" :message="message" :openOnRemote="openOnRemote" @login="onLogin"/>
|
<MkSignin :autoSet="autoSet" :message="message" :openOnRemote="openOnRemote" :initialUsername="initialUsername" @login="onLogin"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
|
@ -34,10 +34,12 @@ withDefaults(defineProps<{
|
||||||
autoSet?: boolean;
|
autoSet?: boolean;
|
||||||
message?: string,
|
message?: string,
|
||||||
openOnRemote?: OpenOnRemoteOptions,
|
openOnRemote?: OpenOnRemoteOptions,
|
||||||
|
initialUsername?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
autoSet: false,
|
autoSet: false,
|
||||||
message: '',
|
message: '',
|
||||||
openOnRemote: undefined,
|
openOnRemote: undefined,
|
||||||
|
initialUsername: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -41,8 +41,7 @@ const props = defineProps<{
|
||||||
.img {
|
.img {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
height: 128px;
|
height: 128px;
|
||||||
aspect-ratio: 1;
|
margin: auto auto 16px;
|
||||||
margin-bottom: 16px;
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { defineAsyncComponent, inject, onMounted, watch, ref } from 'vue';
|
import { defineAsyncComponent, inject, onMounted, watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { genId } from '@/utility/id.js';
|
|
||||||
import XContainer from '../page-editor.container.vue';
|
import XContainer from '../page-editor.container.vue';
|
||||||
|
import { genId } from '@/utility/id.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { deepClone } from '@/utility/clone.js';
|
import { deepClone } from '@/utility/clone.js';
|
||||||
|
@ -35,11 +35,11 @@ import { getPageBlockList } from '@/pages/page-editor/common.js';
|
||||||
const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue'));
|
const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue'));
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: Misskey.entities.PageBlock & { type: 'section'; },
|
modelValue: Extract<Misskey.entities.PageBlock, { type: 'section'; }>,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'section' }): void;
|
(ev: 'update:modelValue', value: Extract<Misskey.entities.PageBlock, { type: 'section'; }>): void;
|
||||||
(ev: 'remove'): void;
|
(ev: 'remove'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ async function rename() {
|
||||||
title: i18n.ts._pages.enterSectionTitle,
|
title: i18n.ts._pages.enterSectionTitle,
|
||||||
default: props.modelValue.title,
|
default: props.modelValue.title,
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled || title == null) return;
|
||||||
emit('update:modelValue', {
|
emit('update:modelValue', {
|
||||||
...props.modelValue,
|
...props.modelValue,
|
||||||
title,
|
title,
|
||||||
|
|
|
@ -109,7 +109,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkPreferenceContainer>
|
</MkPreferenceContainer>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
|
|
||||||
<!--
|
|
||||||
<SearchMarker :keywords="['auto', 'load', 'auto', 'more', 'scroll']">
|
<SearchMarker :keywords="['auto', 'load', 'auto', 'more', 'scroll']">
|
||||||
<MkPreferenceContainer k="enableInfiniteScroll">
|
<MkPreferenceContainer k="enableInfiniteScroll">
|
||||||
<MkSwitch v-model="enableInfiniteScroll">
|
<MkSwitch v-model="enableInfiniteScroll">
|
||||||
|
@ -117,7 +116,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</MkPreferenceContainer>
|
</MkPreferenceContainer>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SearchMarker :keywords="['emoji', 'style', 'native', 'system', 'fluent', 'twemoji']">
|
<SearchMarker :keywords="['emoji', 'style', 'native', 'system', 'fluent', 'twemoji']">
|
||||||
|
|
|
@ -34,7 +34,7 @@ import { instance as meta } from '@/instance.js';
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 80vw; // 100%からshapeの幅を引いている
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,25 +7,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkContainer :showHeader="widgetProps.showHeader" class="mkw-aiscriptApp">
|
<MkContainer :showHeader="widgetProps.showHeader" class="mkw-aiscriptApp">
|
||||||
<template #header>App</template>
|
<template #header>App</template>
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<MkAsUi v-if="root" :component="root" :components="components" size="small"/>
|
<div v-if="isSyntaxError">Syntax error :(</div>
|
||||||
|
<MkAsUi v-else-if="root" :component="root" :components="components" size="small"/>
|
||||||
</div>
|
</div>
|
||||||
</MkContainer>
|
</MkContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import type { Ref } from 'vue';
|
|
||||||
import { Interpreter, Parser } from '@syuilo/aiscript';
|
import { Interpreter, Parser } from '@syuilo/aiscript';
|
||||||
import { useWidgetPropsManager } from './widget.js';
|
import { useWidgetPropsManager } from './widget.js';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
||||||
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
||||||
|
import type { AsUiComponent, AsUiRoot } from '@/aiscript/ui.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
|
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
import MkAsUi from '@/components/MkAsUi.vue';
|
import MkAsUi from '@/components/MkAsUi.vue';
|
||||||
import MkContainer from '@/components/MkContainer.vue';
|
import MkContainer from '@/components/MkContainer.vue';
|
||||||
import { registerAsUiLib } from '@/aiscript/ui.js';
|
import { registerAsUiLib } from '@/aiscript/ui.js';
|
||||||
import type { AsUiComponent, AsUiRoot } from '@/aiscript/ui.js';
|
|
||||||
|
|
||||||
const name = 'aiscriptApp';
|
const name = 'aiscriptApp';
|
||||||
|
|
||||||
|
@ -56,8 +57,11 @@ const parser = new Parser();
|
||||||
|
|
||||||
const root = ref<AsUiRoot>();
|
const root = ref<AsUiRoot>();
|
||||||
const components = ref<Ref<AsUiComponent>[]>([]);
|
const components = ref<Ref<AsUiComponent>[]>([]);
|
||||||
|
const isSyntaxError = ref(false);
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
|
isSyntaxError.value = false;
|
||||||
|
|
||||||
const aiscript = new Interpreter({
|
const aiscript = new Interpreter({
|
||||||
...createAiScriptEnv({
|
...createAiScriptEnv({
|
||||||
storageKey: 'widget',
|
storageKey: 'widget',
|
||||||
|
@ -80,10 +84,7 @@ async function run() {
|
||||||
try {
|
try {
|
||||||
ast = parser.parse(widgetProps.script);
|
ast = parser.parse(widgetProps.script);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
os.alert({
|
isSyntaxError.value = true;
|
||||||
type: 'error',
|
|
||||||
text: 'Syntax error :(',
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -52,7 +52,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
|
||||||
|
|
||||||
const parser = new Parser();
|
const parser = new Parser();
|
||||||
|
|
||||||
const run = async () => {
|
async function run() {
|
||||||
const aiscript = new Interpreter(createAiScriptEnv({
|
const aiscript = new Interpreter(createAiScriptEnv({
|
||||||
storageKey: 'widget',
|
storageKey: 'widget',
|
||||||
token: $i?.token,
|
token: $i?.token,
|
||||||
|
@ -84,7 +84,7 @@ const run = async () => {
|
||||||
text: err instanceof Error ? err.message : String(err),
|
text: err instanceof Error ? err.message : String(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -31,7 +31,7 @@ import { ref, watch, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { useWidgetPropsManager } from './widget.js';
|
import { useWidgetPropsManager } from './widget.js';
|
||||||
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
||||||
import MarqueeText from '@/components/MkMarqueeText.vue';
|
import MkMarqueeText from '@/components/MkMarqueeText.vue';
|
||||||
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
||||||
import MkContainer from '@/components/MkContainer.vue';
|
import MkContainer from '@/components/MkContainer.vue';
|
||||||
import { shuffle } from '@/utility/shuffle.js';
|
import { shuffle } from '@/utility/shuffle.js';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2025.8.0",
|
"version": "2025.9.0-alpha.1",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
|
|
Loading…
Reference in New Issue