Resolve #5755
This commit is contained in:
parent
36b9a0d42f
commit
11cc9cbc7c
28 changed files with 195 additions and 63 deletions
|
@ -481,6 +481,8 @@ descendingOrder: "降順"
|
|||
scratchpad: "スクラッチパッド"
|
||||
scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。"
|
||||
output: "出力"
|
||||
script: "スクリプト"
|
||||
disablePagesScript: "Pagesのスクリプトを無効にする"
|
||||
|
||||
_theme:
|
||||
explore: "テーマを探す"
|
||||
|
@ -813,6 +815,9 @@ _pages:
|
|||
message: "押したときに表示するメッセージ"
|
||||
variable: "送信する変数"
|
||||
no-variable: "なし"
|
||||
callAiScript: "AiScript呼び出し"
|
||||
_callAiScript:
|
||||
functionName: "関数名"
|
||||
|
||||
radioButton: "選択肢"
|
||||
_radioButton:
|
||||
|
@ -975,6 +980,7 @@ _pages:
|
|||
_splitStrByLine:
|
||||
arg1: "テキスト"
|
||||
ref: "変数"
|
||||
aiScriptVar: "AiScript変数"
|
||||
fn: "関数"
|
||||
_fn:
|
||||
slots: "スロット"
|
||||
|
|
14
migration/1586708940386-pageAiScript.ts
Normal file
14
migration/1586708940386-pageAiScript.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class pageAiScript1586708940386 implements MigrationInterface {
|
||||
name = 'pageAiScript1586708940386'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "page" ADD "script" character varying(16384) NOT NULL DEFAULT ''`, undefined);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "script"`, undefined);
|
||||
}
|
||||
|
||||
}
|
|
@ -42,7 +42,7 @@
|
|||
"@koa/cors": "3.0.0",
|
||||
"@koa/multer": "2.0.2",
|
||||
"@koa/router": "8.0.8",
|
||||
"@syuilo/aiscript": "0.1.2",
|
||||
"@syuilo/aiscript": "0.1.4",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.12.1",
|
||||
"@types/cbor": "5.0.0",
|
||||
|
|
|
@ -28,7 +28,7 @@ export default Vue.extend({
|
|||
text: this.script.interpolate(this.value.content)
|
||||
});
|
||||
} else if (this.value.action === 'resetRandom') {
|
||||
this.script.aiScript.updateRandomSeed(Math.random());
|
||||
this.script.aoiScript.updateRandomSeed(Math.random());
|
||||
this.script.eval();
|
||||
} else if (this.value.action === 'pushEvent') {
|
||||
this.$root.api('page-push', {
|
||||
|
@ -43,6 +43,8 @@ export default Vue.extend({
|
|||
type: 'success',
|
||||
text: this.script.interpolate(this.value.message)
|
||||
});
|
||||
} else if (this.value.action === 'callAiScript') {
|
||||
this.script.callAiScript(this.value.fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export default Vue.extend({
|
|||
},
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.aoiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -27,7 +27,7 @@ export default Vue.extend({
|
|||
},
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.aoiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ export default Vue.extend({
|
|||
},
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.aoiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export default Vue.extend({
|
|||
},
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.aoiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export default Vue.extend({
|
|||
},
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.aoiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export default Vue.extend({
|
|||
},
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.aoiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,30 +6,57 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { AiScript, parse, values } from '@syuilo/aiscript';
|
||||
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart } from '@fortawesome/free-regular-svg-icons';
|
||||
import i18n from '../../i18n';
|
||||
import XBlock from './page.block.vue';
|
||||
import { ASEvaluator } from '../../scripts/aoiscript/evaluator';
|
||||
import { collectPageVars } from '../../scripts/collect-page-vars';
|
||||
import { url } from '../../config';
|
||||
|
||||
class Script {
|
||||
public aiScript: ASEvaluator;
|
||||
public aoiScript: ASEvaluator;
|
||||
private onError: any;
|
||||
public vars: Record<string, any>;
|
||||
public page: Record<string, any>;
|
||||
|
||||
constructor(page, aiScript, onError) {
|
||||
constructor(page, aoiScript, onError, cb) {
|
||||
this.page = page;
|
||||
this.aiScript = aiScript;
|
||||
this.aoiScript = aoiScript;
|
||||
this.onError = onError;
|
||||
this.eval();
|
||||
|
||||
if (this.page.script) {
|
||||
let ast;
|
||||
try {
|
||||
ast = parse(this.page.script);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
/*this.$root.dialog({
|
||||
type: 'error',
|
||||
text: 'Syntax error :('
|
||||
});*/
|
||||
return;
|
||||
}
|
||||
this.aoiScript.aiscript.exec(ast).then(() => {
|
||||
this.eval();
|
||||
cb();
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
/*this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e
|
||||
});*/
|
||||
});
|
||||
} else {
|
||||
this.eval();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
public eval() {
|
||||
try {
|
||||
this.vars = this.aiScript.evaluateVars();
|
||||
this.vars = this.aoiScript.evaluateVars();
|
||||
} catch (e) {
|
||||
this.onError(e);
|
||||
}
|
||||
|
@ -42,6 +69,10 @@ class Script {
|
|||
return v == null ? 'NULL' : v.toString();
|
||||
});
|
||||
}
|
||||
|
||||
public callAiScript(fn: string) {
|
||||
this.aoiScript.aiscript.execFn(this.aoiScript.aiscript.scope.get(fn), []);
|
||||
}
|
||||
}
|
||||
|
||||
export default Vue.extend({
|
||||
|
@ -67,14 +98,21 @@ export default Vue.extend({
|
|||
|
||||
created() {
|
||||
const pageVars = this.getPageVars();
|
||||
this.script = new Script(this.page, new ASEvaluator(this.page.variables, pageVars, {
|
||||
|
||||
const s = new Script(this.page, new ASEvaluator(this, this.page.variables, pageVars, {
|
||||
randomSeed: Math.random(),
|
||||
visitor: this.$store.state.i,
|
||||
page: this.page,
|
||||
url: url
|
||||
}), e => {
|
||||
console.dir(e);
|
||||
}, () => {
|
||||
this.script = s;
|
||||
});
|
||||
|
||||
s.aoiScript.aiscript.scope.opts.onUpdated = (name, value) => {
|
||||
s.eval();
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<option value="dialog">{{ $t('_pages.blocks._button._action.dialog') }}</option>
|
||||
<option value="resetRandom">{{ $t('_pages.blocks._button._action.resetRandom') }}</option>
|
||||
<option value="pushEvent">{{ $t('_pages.blocks._button._action.pushEvent') }}</option>
|
||||
<option value="callAiScript">{{ $t('_pages.blocks._button._action.callAiScript') }}</option>
|
||||
</mk-select>
|
||||
<template v-if="value.action === 'dialog'">
|
||||
<mk-input v-model="value.content"><span>{{ $t('_pages.blocks._button._action._dialog.content') }}</span></mk-input>
|
||||
|
@ -20,15 +21,18 @@
|
|||
<mk-select v-model="value.var">
|
||||
<template #label>{{ $t('_pages.blocks._button._action._pushEvent.variable') }}</template>
|
||||
<option :value="null">{{ $t('_pages.blocks._button._action._pushEvent.no-variable') }}</option>
|
||||
<option v-for="v in aiScript.getVarsByType()" :value="v.name">{{ v.name }}</option>
|
||||
<option v-for="v in aoiScript.getVarsByType()" :value="v.name">{{ v.name }}</option>
|
||||
<optgroup :label="$t('_pages.script.pageVariables')">
|
||||
<option v-for="v in aiScript.getPageVarsByType()" :value="v">{{ v }}</option>
|
||||
<option v-for="v in aoiScript.getPageVarsByType()" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$t('_pages.script.enviromentVariables')">
|
||||
<option v-for="v in aiScript.getEnvVarsByType()" :value="v">{{ v }}</option>
|
||||
<option v-for="v in aoiScript.getEnvVarsByType()" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
</mk-select>
|
||||
</template>
|
||||
<template v-else-if="value.action === 'callAiScript'">
|
||||
<mk-input v-model="value.fn"><span>{{ $t('_pages.blocks._button._action._callAiScript.functionName') }}</span></mk-input>
|
||||
</template>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
@ -53,7 +57,7 @@ export default Vue.extend({
|
|||
value: {
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
aoiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
@ -72,6 +76,7 @@ export default Vue.extend({
|
|||
if (this.value.message == null) Vue.set(this.value, 'message', null);
|
||||
if (this.value.primary == null) Vue.set(this.value, 'primary', false);
|
||||
if (this.value.var == null) Vue.set(this.value, 'var', null);
|
||||
if (this.value.fn == null) Vue.set(this.value, 'fn', null);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -10,16 +10,16 @@
|
|||
<section class="romcojzs">
|
||||
<mk-select v-model="value.var">
|
||||
<template #label>{{ $t('_pages.blocks._if.variable') }}</template>
|
||||
<option v-for="v in aiScript.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
|
||||
<option v-for="v in aoiScript.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
|
||||
<optgroup :label="$t('_pages.script.pageVariables')">
|
||||
<option v-for="v in aiScript.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
|
||||
<option v-for="v in aoiScript.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$t('_pages.script.enviromentVariables')">
|
||||
<option v-for="v in aiScript.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
|
||||
<option v-for="v in aoiScript.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
</mk-select>
|
||||
|
||||
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
|
||||
<x-blocks class="children" v-model="value.children" :aoi-script="aoiScript"/>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
@ -45,7 +45,7 @@ export default Vue.extend({
|
|||
value: {
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
aoiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</template>
|
||||
|
||||
<section class="ilrvjyvi">
|
||||
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
|
||||
<x-blocks class="children" v-model="value.children" :aoi-script="aoiScript"/>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
@ -37,7 +37,7 @@ export default Vue.extend({
|
|||
value: {
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
aoiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faAlignLeft"/> {{ $t('_pages.blocks.text') }}</template>
|
||||
|
||||
<section class="ihymsbbe">
|
||||
<section class="vckmsadr">
|
||||
<textarea v-model="value.text"></textarea>
|
||||
</section>
|
||||
</x-container>
|
||||
|
@ -40,7 +40,7 @@ export default Vue.extend({
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ihymsbbe {
|
||||
.vckmsadr {
|
||||
> textarea {
|
||||
display: block;
|
||||
-webkit-appearance: none;
|
||||
|
@ -55,6 +55,7 @@ export default Vue.extend({
|
|||
background: transparent;
|
||||
color: var(--fg);
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -55,6 +55,7 @@ export default Vue.extend({
|
|||
background: transparent;
|
||||
color: var(--fg);
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<x-draggable tag="div" :list="blocks" handle=".drag-handle" :group="{ name: 'blocks' }" animation="150" swap-threshold="0.5">
|
||||
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :ai-script="aiScript"/>
|
||||
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :aoi-script="aoiScript"/>
|
||||
</x-draggable>
|
||||
</template>
|
||||
|
||||
|
@ -31,7 +31,7 @@ export default Vue.extend({
|
|||
type: Array,
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
aoiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable">
|
||||
<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
|
||||
<template #func>
|
||||
<button @click="changeType()">
|
||||
<button @click="changeType()" class="_button">
|
||||
<fa :icon="faPencilAlt"/>
|
||||
</button>
|
||||
</template>
|
||||
|
@ -24,30 +24,33 @@
|
|||
</section>
|
||||
<section v-else-if="value.type === 'ref'" class="hpdwcrvs">
|
||||
<select v-model="value.value">
|
||||
<option v-for="v in aiScript.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
|
||||
<option v-for="v in aoiScript.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
|
||||
<optgroup :label="$t('_pages.script.argVariables')">
|
||||
<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$t('_pages.script.pageVariables')">
|
||||
<option v-for="v in aiScript.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
|
||||
<option v-for="v in aoiScript.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$t('_pages.script.enviromentVariables')">
|
||||
<option v-for="v in aiScript.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
|
||||
<option v-for="v in aoiScript.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</section>
|
||||
<section v-else-if="value.type === 'aiScriptVar'" class="tbwccoaw">
|
||||
<input v-model="value.value"/>
|
||||
</section>
|
||||
<section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
|
||||
<mk-textarea v-model="slots">
|
||||
<span>{{ $t('_pages.script.blocks._fn.slots') }}</span>
|
||||
<template #desc>{{ $t('_pages.script.blocks._fn.slots-info') }}</template>
|
||||
</mk-textarea>
|
||||
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :ai-script="aiScript" :fn-slots="value.value.slots" :name="name"/>
|
||||
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :aoi-script="aoiScript" :fn-slots="value.value.slots" :name="name"/>
|
||||
</section>
|
||||
<section v-else-if="value.type.startsWith('fn:')" class="" style="padding:16px;">
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aiScript.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :ai-script="aiScript" :name="name" :key="i"/>
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aoiScript.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :aoi-script="aoiScript" :name="name" :key="i"/>
|
||||
</section>
|
||||
<section v-else class="" style="padding:16px;">
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :ai-script="aiScript" :name="name" :fn-slots="fnSlots" :key="i"/>
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :aoi-script="aoiScript" :name="name" :fn-slots="fnSlots" :key="i"/>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
@ -85,7 +88,7 @@ export default Vue.extend({
|
|||
required: false,
|
||||
default: false
|
||||
},
|
||||
aiScript: {
|
||||
aoiScript: {
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
|
@ -153,7 +156,7 @@ export default Vue.extend({
|
|||
|
||||
if (this.value.type && this.value.type.startsWith('fn:')) {
|
||||
const fnName = this.value.type.split(':')[1];
|
||||
const fn = this.aiScript.getVarByName(fnName);
|
||||
const fn = this.aoiScript.getVarByName(fnName);
|
||||
|
||||
const empties = [];
|
||||
for (let i = 0; i < fn.value.slots.length; i++) {
|
||||
|
@ -199,9 +202,9 @@ export default Vue.extend({
|
|||
deep: true
|
||||
});
|
||||
|
||||
this.$watch('aiScript.variables', () => {
|
||||
this.$watch('aoiScript.variables', () => {
|
||||
if (this.type != null && this.value) {
|
||||
this.error = this.aiScript.typeCheck(this.value);
|
||||
this.error = this.aoiScript.typeCheck(this.value);
|
||||
}
|
||||
}, {
|
||||
deep: true
|
||||
|
@ -223,7 +226,7 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
_getExpectedType(slot: number) {
|
||||
return this.aiScript.getExpectedType(this.value, slot);
|
||||
return this.aoiScript.getExpectedType(this.value, slot);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -258,6 +261,7 @@ export default Vue.extend({
|
|||
font-size: 16px;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
> textarea {
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<x-blocks class="content" v-model="content" :ai-script="aiScript"/>
|
||||
<x-blocks class="content" v-model="content" :aoi-script="aoiScript"/>
|
||||
|
||||
<mk-button @click="add()" v-if="!readonly"><fa :icon="faPlus"/></mk-button>
|
||||
</section>
|
||||
|
@ -62,7 +62,7 @@
|
|||
@input="v => updateVariable(v)"
|
||||
@remove="() => removeVariable(variable)"
|
||||
:key="variable.name"
|
||||
:ai-script="aiScript"
|
||||
:aoi-script="aoiScript"
|
||||
:name="variable.name"
|
||||
:title="variable.name"
|
||||
:draggable="true"
|
||||
|
@ -73,11 +73,10 @@
|
|||
</div>
|
||||
</mk-container>
|
||||
|
||||
<mk-container :body-togglable="true" :expanded="false">
|
||||
<template #header><fa :icon="faCode"/> {{ $t('_pages.inspector') }}</template>
|
||||
<div style="padding:0 32px 32px 32px;">
|
||||
<mk-textarea :value="JSON.stringify(content, null, 2)" readonly tall>{{ $t('_pages.content') }}</mk-textarea>
|
||||
<mk-textarea :value="JSON.stringify(variables, null, 2)" readonly tall>{{ $t('_pages.variables') }}</mk-textarea>
|
||||
<mk-container :body-togglable="true" :expanded="true">
|
||||
<template #header><fa :icon="faCode"/> {{ $t('script') }}</template>
|
||||
<div>
|
||||
<prism-editor v-model="script" :line-numbers="false" language="js"/>
|
||||
</div>
|
||||
</mk-container>
|
||||
</div>
|
||||
|
@ -86,6 +85,9 @@
|
|||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as XDraggable from 'vuedraggable';
|
||||
import "prismjs";
|
||||
import "prismjs/themes/prism.css";
|
||||
import PrismEditor from 'vue-prism-editor';
|
||||
import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
@ -108,7 +110,7 @@ export default Vue.extend({
|
|||
i18n,
|
||||
|
||||
components: {
|
||||
XDraggable, XVariable, XBlocks, MkTextarea, MkContainer, MkButton, MkSelect, MkSwitch, MkInput
|
||||
XDraggable, XVariable, XBlocks, MkTextarea, MkContainer, MkButton, MkSelect, MkSwitch, MkInput, PrismEditor
|
||||
},
|
||||
|
||||
props: {
|
||||
|
@ -143,7 +145,8 @@ export default Vue.extend({
|
|||
alignCenter: false,
|
||||
hideTitleWhenPinned: false,
|
||||
variables: [],
|
||||
aiScript: null,
|
||||
aoiScript: null,
|
||||
script: '',
|
||||
showOptions: false,
|
||||
url,
|
||||
faPlus, faICursor, faSave, faStickyNote, faMagic, faCog, faTrashAlt, faExternalLinkSquareAlt, faCode
|
||||
|
@ -163,14 +166,14 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
async created() {
|
||||
this.aiScript = new ASTypeChecker();
|
||||
this.aoiScript = new ASTypeChecker();
|
||||
|
||||
this.$watch('variables', () => {
|
||||
this.aiScript.variables = this.variables;
|
||||
this.aoiScript.variables = this.variables;
|
||||
}, { deep: true });
|
||||
|
||||
this.$watch('content', () => {
|
||||
this.aiScript.pageVars = collectPageVars(this.content);
|
||||
this.aoiScript.pageVars = collectPageVars(this.content);
|
||||
}, { deep: true });
|
||||
|
||||
if (this.initPageId) {
|
||||
|
@ -193,6 +196,7 @@ export default Vue.extend({
|
|||
this.currentName = this.page.name;
|
||||
this.summary = this.page.summary;
|
||||
this.font = this.page.font;
|
||||
this.script = this.page.script;
|
||||
this.hideTitleWhenPinned = this.page.hideTitleWhenPinned;
|
||||
this.alignCenter = this.page.alignCenter;
|
||||
this.content = this.page.content;
|
||||
|
@ -223,6 +227,7 @@ export default Vue.extend({
|
|||
name: this.name.trim(),
|
||||
summary: this.summary,
|
||||
font: this.font,
|
||||
script: this.script,
|
||||
hideTitleWhenPinned: this.hideTitleWhenPinned,
|
||||
alignCenter: this.alignCenter,
|
||||
content: this.content,
|
||||
|
@ -317,7 +322,7 @@ export default Vue.extend({
|
|||
|
||||
name = name.trim();
|
||||
|
||||
if (this.aiScript.isUsedName(name)) {
|
||||
if (this.aoiScript.isUsedName(name)) {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('_pages.variableNameIsAlreadyUsed')
|
||||
|
@ -382,7 +387,7 @@ export default Vue.extend({
|
|||
} else {
|
||||
list.push({
|
||||
category: block.category,
|
||||
label: this.$t(`script.categories.${block.category}`),
|
||||
label: this.$t(`_pages.script.categories.${block.category}`),
|
||||
items: [{
|
||||
value: block.type,
|
||||
text: this.$t(`_pages.script.blocks.${block.type}`)
|
||||
|
@ -394,7 +399,7 @@ export default Vue.extend({
|
|||
const userFns = this.variables.filter(x => x.type === 'fn');
|
||||
if (userFns.length > 0) {
|
||||
list.unshift({
|
||||
label: this.$t(`script.categories.fn`),
|
||||
label: this.$t(`_pages.script.categories.fn`),
|
||||
items: userFns.map(v => ({
|
||||
value: 'fn:' + v.name,
|
||||
text: v.name
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faTerminal, faPlay } from '@fortawesome/free-solid-svg-icons';
|
||||
import "prismjs";
|
||||
import "prismjs/themes/prism.css";
|
||||
import { faTerminal, faPlay } from '@fortawesome/free-solid-svg-icons';
|
||||
import PrismEditor from 'vue-prism-editor';
|
||||
import { AiScript, parse, utils, values } from '@syuilo/aiscript';
|
||||
import i18n from '../i18n';
|
||||
|
|
|
@ -2,6 +2,8 @@ import autobind from 'autobind-decorator';
|
|||
import * as seedrandom from 'seedrandom';
|
||||
import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
|
||||
import { version } from '../../config';
|
||||
import { AiScript, utils, parse, values } from '@syuilo/aiscript';
|
||||
import { createAiScriptEnv } from '../create-aiscript-env';
|
||||
|
||||
type Fn = {
|
||||
slots: string[];
|
||||
|
@ -15,15 +17,41 @@ export class ASEvaluator {
|
|||
private variables: Variable[];
|
||||
private pageVars: PageVar[];
|
||||
private envVars: Record<keyof typeof envVarsDef, any>;
|
||||
public aiscript: AiScript;
|
||||
private pageVarUpdatedCallback;
|
||||
|
||||
private opts: {
|
||||
randomSeed: string; visitor?: any; page?: any; url?: string;
|
||||
};
|
||||
|
||||
constructor(variables: Variable[], pageVars: PageVar[], opts: ASEvaluator['opts']) {
|
||||
constructor(vm: any, variables: Variable[], pageVars: PageVar[], opts: ASEvaluator['opts']) {
|
||||
this.variables = variables;
|
||||
this.pageVars = pageVars;
|
||||
this.opts = opts;
|
||||
this.aiscript = new AiScript({ ...createAiScriptEnv(vm, {
|
||||
storageKey: 'pages:' + opts.page.id
|
||||
}), ...{
|
||||
'MkPages:updated': values.FN_NATIVE(([callback]) => {
|
||||
this.pageVarUpdatedCallback = callback;
|
||||
})
|
||||
}}, {
|
||||
in: (q) => {
|
||||
return new Promise(ok => {
|
||||
vm.$root.dialog({
|
||||
title: q,
|
||||
input: {}
|
||||
}).then(({ canceled, result: a }) => {
|
||||
ok(a);
|
||||
});
|
||||
});
|
||||
},
|
||||
out: (value) => {
|
||||
console.log(value);
|
||||
},
|
||||
log: (type, params) => {
|
||||
},
|
||||
maxStep: 16384
|
||||
});
|
||||
|
||||
const date = new Date();
|
||||
|
||||
|
@ -50,6 +78,9 @@ export class ASEvaluator {
|
|||
const pageVar = this.pageVars.find(v => v.name === name);
|
||||
if (pageVar !== undefined) {
|
||||
pageVar.value = value;
|
||||
if (this.pageVarUpdatedCallback) {
|
||||
this.aiscript.execFn(this.pageVarUpdatedCallback, [values.STR(name), utils.jsToVal(value)]);
|
||||
}
|
||||
} else {
|
||||
throw new AoiScriptError(`No such page var '${name}'`);
|
||||
}
|
||||
|
@ -110,6 +141,10 @@ export class ASEvaluator {
|
|||
return scope.getState(block.value);
|
||||
}
|
||||
|
||||
if (block.type === 'aiScriptVar') {
|
||||
return utils.valToJs(this.aiscript.scope.get(block.value));
|
||||
}
|
||||
|
||||
if (isFnBlock(block)) { // ユーザー関数定義
|
||||
return {
|
||||
slots: block.value.slots.map(x => x.name),
|
||||
|
|
|
@ -95,6 +95,7 @@ export const literalDefs: Record<string, { out: any; category: string; icon: any
|
|||
textList: { out: 'stringArray', category: 'value', icon: faList, },
|
||||
number: { out: 'number', category: 'value', icon: faSortNumericUp, },
|
||||
ref: { out: null, category: 'value', icon: faMagic, },
|
||||
aiScriptVar: { out: null, category: 'value', icon: faMagic, },
|
||||
fn: { out: 'function', category: 'value', icon: faSquareRootAlt, },
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { utils, values } from '@syuilo/aiscript';
|
||||
|
||||
export function createAiScriptEnv(vm, opts) {
|
||||
let apiRequests = 0;
|
||||
return {
|
||||
USER_ID: values.STR(vm.$store.state.i.id),
|
||||
USER_USERNAME: values.STR(vm.$store.state.i.username),
|
||||
|
@ -21,6 +22,8 @@ export function createAiScriptEnv(vm, opts) {
|
|||
return confirm.canceled ? values.FALSE : values.TRUE
|
||||
}),
|
||||
'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => {
|
||||
apiRequests++;
|
||||
if (apiRequests > 16) return values.NULL;
|
||||
const res = await vm.$root.api(ep.value, utils.valToJs(param), token || null);
|
||||
return utils.jsToVal(res);
|
||||
}),
|
||||
|
|
|
@ -85,6 +85,12 @@ export class Page {
|
|||
})
|
||||
public variables: Record<string, any>[];
|
||||
|
||||
@Column('varchar', {
|
||||
length: 16384,
|
||||
default: ''
|
||||
})
|
||||
public script: string;
|
||||
|
||||
/**
|
||||
* public ... 公開
|
||||
* followers ... フォロワーのみ
|
||||
|
|
|
@ -74,6 +74,7 @@ export class PageRepository extends Repository<Page> {
|
|||
hideTitleWhenPinned: page.hideTitleWhenPinned,
|
||||
alignCenter: page.alignCenter,
|
||||
font: page.font,
|
||||
script: page.script,
|
||||
eyeCatchingImageId: page.eyeCatchingImageId,
|
||||
eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
|
||||
attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)),
|
||||
|
|
|
@ -44,6 +44,10 @@ export const meta = {
|
|||
validator: $.arr($.obj())
|
||||
},
|
||||
|
||||
script: {
|
||||
validator: $.str,
|
||||
},
|
||||
|
||||
eyeCatchingImageId: {
|
||||
validator: $.optional.nullable.type(ID),
|
||||
},
|
||||
|
@ -115,6 +119,7 @@ export default define(meta, async (ps, user) => {
|
|||
summary: ps.summary,
|
||||
content: ps.content,
|
||||
variables: ps.variables,
|
||||
script: ps.script,
|
||||
eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null,
|
||||
userId: user.id,
|
||||
visibility: 'public',
|
||||
|
|
|
@ -51,6 +51,10 @@ export const meta = {
|
|||
validator: $.arr($.obj())
|
||||
},
|
||||
|
||||
script: {
|
||||
validator: $.str,
|
||||
},
|
||||
|
||||
eyeCatchingImageId: {
|
||||
validator: $.optional.nullable.type(ID),
|
||||
},
|
||||
|
@ -132,6 +136,7 @@ export default define(meta, async (ps, user) => {
|
|||
summary: ps.name === undefined ? page.summary : ps.summary,
|
||||
content: ps.content,
|
||||
variables: ps.variables,
|
||||
script: ps.script,
|
||||
alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter,
|
||||
hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned,
|
||||
font: ps.font === undefined ? page.font : ps.font,
|
||||
|
|
|
@ -144,10 +144,10 @@
|
|||
dependencies:
|
||||
type-detect "4.0.8"
|
||||
|
||||
"@syuilo/aiscript@0.1.2":
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.1.2.tgz#65c42793c38707d862b3a64f5edc845789372ade"
|
||||
integrity sha512-W0G/JuVkD9jARPhKFaaHp+59Iv+2LapQ2zKjM08hoB/6hEzHjis0uRbw07TXyughQb17iU452rp1gJEUkXV3Mg==
|
||||
"@syuilo/aiscript@0.1.4":
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@syuilo/aiscript/-/aiscript-0.1.4.tgz#ff027552f32990ae3e29145ce6efe0a7a516b442"
|
||||
integrity sha512-SMDlBInsGTL3DOe0U394X7na0N6ryYg0RGQPPtCVhXkJpVDZiaqUe5vDO+DkRyuRlkmBbN82LWToou19j/Uv8g==
|
||||
dependencies:
|
||||
autobind-decorator "2.4.0"
|
||||
chalk "4.0.0"
|
||||
|
|
Loading…
Reference in a new issue