diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 8ae0628471..7c19c982e0 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -523,6 +523,9 @@ themeEditor: "テーマエディター"
description: "説明"
author: "作者"
leaveConfirm: "未保存の変更があります。破棄しますか?"
+manage: "管理"
+plugins: "プラグイン"
+pluginInstallWarn: "信頼できないプラグインはインストールしないでください。"
deck: "デッキ"
undeck: "デッキ解除"
diff --git a/package.json b/package.json
index ad55e2e035..4a81ed9636 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
"@koa/multer": "3.0.0",
"@koa/router": "9.3.1",
"@sinonjs/fake-timers": "6.0.1",
- "@syuilo/aiscript": "0.7.0",
+ "@syuilo/aiscript": "0.7.2",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.14.0",
"@types/cbor": "5.0.0",
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index badb9f12f3..63a803c7f4 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -89,7 +89,7 @@
diff --git a/src/client/components/user-menu.vue b/src/client/components/user-menu.vue
index 25937fb3c0..cbfa7b346d 100644
--- a/src/client/components/user-menu.vue
+++ b/src/client/components/user-menu.vue
@@ -4,7 +4,7 @@
+
+
diff --git a/src/client/pages/scratchpad.vue b/src/client/pages/scratchpad.vue
index 81d4e60459..025505295b 100644
--- a/src/client/pages/scratchpad.vue
+++ b/src/client/pages/scratchpad.vue
@@ -30,7 +30,7 @@ import PrismEditor from 'vue-prism-editor';
import { AiScript, parse, utils, values } from '@syuilo/aiscript';
import MkContainer from '../components/ui/container.vue';
import MkButton from '../components/ui/button.vue';
-import { createAiScriptEnv } from '../scripts/create-aiscript-env';
+import { createAiScriptEnv } from '../scripts/aiscript/api';
export default Vue.extend({
metaInfo() {
diff --git a/src/client/scripts/create-aiscript-env.ts b/src/client/scripts/aiscript/api.ts
similarity index 71%
rename from src/client/scripts/create-aiscript-env.ts
rename to src/client/scripts/aiscript/api.ts
index dfa38be385..29baa25b1a 100644
--- a/src/client/scripts/create-aiscript-env.ts
+++ b/src/client/scripts/aiscript/api.ts
@@ -40,3 +40,18 @@ export function createAiScriptEnv(vm, opts) {
}),
};
}
+
+export function createPluginEnv(vm, opts) {
+ return {
+ ...createAiScriptEnv(vm, opts),
+ 'Mk:register_post_form_action': values.FN_NATIVE(([title, handler]) => {
+ vm.$store.commit('registerPostFormAction', { pluginId: opts.plugin.id, title: title.value, handler });
+ }),
+ 'Mk:register_user_action': values.FN_NATIVE(([title, handler]) => {
+ vm.$store.commit('registerUserAction', { pluginId: opts.plugin.id, title: title.value, handler });
+ }),
+ 'Mk:register_note_action': values.FN_NATIVE(([title, handler]) => {
+ vm.$store.commit('registerNoteAction', { pluginId: opts.plugin.id, title: title.value, handler });
+ }),
+ };
+}
diff --git a/src/client/scripts/hpml/evaluator.ts b/src/client/scripts/hpml/evaluator.ts
index f1fcdde0e5..a056884368 100644
--- a/src/client/scripts/hpml/evaluator.ts
+++ b/src/client/scripts/hpml/evaluator.ts
@@ -3,7 +3,7 @@ import * as seedrandom from 'seedrandom';
import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
import { version } from '../../config';
import { AiScript, utils, values } from '@syuilo/aiscript';
-import { createAiScriptEnv } from '../create-aiscript-env';
+import { createAiScriptEnv } from '../aiscript/api';
import { collectPageVars } from '../collect-page-vars';
import { initLib } from './lib';
diff --git a/src/client/store.ts b/src/client/store.ts
index eaa8ea6a69..31febc782b 100644
--- a/src/client/store.ts
+++ b/src/client/store.ts
@@ -3,6 +3,7 @@ import createPersistedState from 'vuex-persistedstate';
import * as nestedProperty from 'nested-property';
import { faTerminal, faHashtag, faBroadcastTower, faFireAlt, faSearch, faStar, faAt, faListUl, faUserClock, faUsers, faCloud, faGamepad, faFileAlt, faSatellite, faDoorClosed, faColumns } from '@fortawesome/free-solid-svg-icons';
import { faBell, faEnvelope, faComments } from '@fortawesome/free-regular-svg-icons';
+import { AiScript, utils, values } from '@syuilo/aiscript';
import { apiUrl, deckmode } from './config';
import { erase } from '../prelude/array';
@@ -43,6 +44,7 @@ export const defaultDeviceUserSettings = {
columns: [],
layout: [],
},
+ plugins: [],
};
export const defaultDeviceSettings = {
@@ -93,7 +95,13 @@ export default () => new Vuex.Store({
state: {
i: null,
pendingApiRequestsCount: 0,
- spinner: null
+ spinner: null,
+
+ // Plugin
+ pluginContexts: new Map(),
+ postFormActions: [],
+ userActions: [],
+ noteActions: [],
},
getters: {
@@ -224,8 +232,38 @@ export default () => new Vuex.Store({
state.i = x;
},
- updateIKeyValue(state, x) {
- state.i[x.key] = x.value;
+ updateIKeyValue(state, { key, value }) {
+ state.i[key] = value;
+ },
+
+ initPlugin(state, { plugin, aiscript }) {
+ state.pluginContexts.set(plugin.id, aiscript);
+ },
+
+ registerPostFormAction(state, { pluginId, title, handler }) {
+ state.postFormActions.push({
+ title, handler: (form, update) => {
+ state.pluginContexts.get(pluginId).execFn(handler, [utils.jsToVal(form), values.FN_NATIVE(([key, value]) => {
+ update(key.value, value.value);
+ })]);
+ }
+ });
+ },
+
+ registerUserAction(state, { pluginId, title, handler }) {
+ state.userActions.push({
+ title, handler: (user) => {
+ state.pluginContexts.get(pluginId).execFn(handler, [utils.jsToVal(user)]);
+ }
+ });
+ },
+
+ registerNoteAction(state, { pluginId, title, handler }) {
+ state.noteActions.push({
+ title, handler: (note) => {
+ state.pluginContexts.get(pluginId).execFn(handler, [utils.jsToVal(note)]);
+ }
+ });
},
},
@@ -546,6 +584,21 @@ export default () => new Vuex.Store({
column = x;
},
//#endregion
+
+ installPlugin(state, { meta, ast }) {
+ state.plugins.push({
+ id: meta.id,
+ name: meta.name,
+ version: meta.version,
+ author: meta.author,
+ description: meta.description,
+ ast: ast
+ });
+ },
+
+ uninstallPlugin(state, id) {
+ state.plugins = state.plugins.filter(x => x.id != id);
+ },
}
},
diff --git a/yarn.lock b/yarn.lock
index 2fd02e8dbc..ad6cb9ca37 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -197,10 +197,10 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
-"@syuilo/aiscript@0.7.0":
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.7.0.tgz#1394511a789891e844d32e536a203fe0d92b3039"
- integrity sha512-X4TaP/FO7RD8MpFSPDFwKAI4KX7byn8ApqmSSmf2bxcwCTcdevsbyxsLrvkbNaWclIoqTgXwtJjY+2Tc2exeXA==
+"@syuilo/aiscript@0.7.2":
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.7.2.tgz#2f30adb14ffa9f1180af83c059927ab306b175a5"
+ integrity sha512-l8HVTJTq9KLzDqGswOIGlBepkacudUp70EScrLjL7nEL2NKcti7Ui5fwZCrmxazxgGz6NrVNX5UBIOFFyrwr0A==
dependencies:
autobind-decorator "2.4.0"
chalk "4.0.0"