From b2fb92cf0fcf193e46b423b5a0cec8d8c3215c5d Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Sat, 6 Feb 2021 21:36:47 +0900 Subject: [PATCH] Add AiScript console widget --- locales/ja-JP.yml | 1 + src/client/widgets/aiscript.vue | 164 ++++++++++++++++++++++++++++++++ src/client/widgets/index.ts | 2 + src/client/widgets/memo.vue | 12 ++- 4 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/client/widgets/aiscript.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 2233fa27f1..3f8542dfd0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1067,6 +1067,7 @@ _widgets: onlineUsers: "オンラインユーザー" jobQueue: "ジョブã‚ュー" serverMetric: "サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹" + aiscript: "AiScriptコンソール" _cw: hide: "éš ã™" diff --git a/src/client/widgets/aiscript.vue b/src/client/widgets/aiscript.vue new file mode 100644 index 0000000000..4e788b4b4a --- /dev/null +++ b/src/client/widgets/aiscript.vue @@ -0,0 +1,164 @@ +<template> +<MkContainer :show-header="props.showHeader"> + <template #header><Fa :icon="faTerminal"/>{{ $ts._widgets.aiscript }}</template> + + <div class="uylguesu _monospace"> + <textarea v-model="props.script" placeholder="(1 + 1)"></textarea> + <button @click="run" class="_buttonPrimary">RUN</button> + <div class="logs"> + <div v-for="log in logs" class="log" :key="log.id" :class="{ print: log.print }">{{ log.text }}</div> + </div> + </div> +</MkContainer> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import { faTerminal } from '@fortawesome/free-solid-svg-icons'; +import MkContainer from '@/components/ui/container.vue'; +import define from './define'; +import * as os from '@/os'; +import { AiScript, parse, utils } from '@syuilo/aiscript'; +import { createAiScriptEnv } from '@/scripts/aiscript/api'; + +const widget = define({ + name: 'aiscript', + props: () => ({ + showHeader: { + type: 'boolean', + default: true, + }, + script: { + type: 'string', + multiline: true, + default: '(1 + 1)', + hidden: true, + }, + }) +}); + +export default defineComponent({ + extends: widget, + components: { + MkContainer + }, + + data() { + return { + logs: [], + faTerminal + }; + }, + + methods: { + async run() { + this.logs = []; + const aiscript = new AiScript(createAiScriptEnv({ + storageKey: 'widget' + }), { + in: (q) => { + return new Promise(ok => { + os.dialog({ + title: q, + input: {} + }).then(({ canceled, result: a }) => { + ok(a); + }); + }); + }, + out: (value) => { + this.logs.push({ + id: Math.random(), + text: value.type === 'str' ? value.value : utils.valToString(value), + print: true + }); + }, + log: (type, params) => { + switch (type) { + case 'end': this.logs.push({ + id: Math.random(), + text: utils.valToString(params.val, true), + print: false + }); break; + default: break; + } + } + }); + + let ast; + try { + ast = parse(this.props.script); + } catch (e) { + os.dialog({ + type: 'error', + text: 'Syntax error :(' + }); + return; + } + try { + await aiscript.exec(ast); + } catch (e) { + os.dialog({ + type: 'error', + text: e + }); + } + }, + } +}); +</script> + +<style lang="scss" scoped> +.uylguesu { + text-align: right; + + > textarea { + display: block; + width: 100%; + max-width: 100%; + min-width: 100%; + padding: 16px; + color: var(--fg); + background: transparent; + border: none; + border-bottom: solid 1px var(--divider); + border-radius: 0; + box-sizing: border-box; + font: inherit; + + &:focus { + outline: none; + } + } + + > button { + display: inline-block; + margin: 8px; + padding: 0 10px; + height: 28px; + outline: none; + border-radius: 4px; + + &:disabled { + opacity: 0.7; + cursor: default; + } + } + + > .logs { + border-top: solid 1px var(--divider); + text-align: left; + padding: 16px; + + &:empty { + display: none; + } + + > .log { + &:not(.print) { + opacity: 0.7; + } + } + } +} +</style> diff --git a/src/client/widgets/index.ts b/src/client/widgets/index.ts index 0c7e824306..38cb85494a 100644 --- a/src/client/widgets/index.ts +++ b/src/client/widgets/index.ts @@ -18,6 +18,7 @@ export default function(app: App) { app.component('MkwOnlineUsers', defineAsyncComponent(() => import('./online-users.vue'))); app.component('MkwJobQueue', defineAsyncComponent(() => import('./job-queue.vue'))); app.component('MkwButton', defineAsyncComponent(() => import('./button.vue'))); + app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue'))); } export const widgets = [ @@ -38,4 +39,5 @@ export const widgets = [ 'onlineUsers', 'jobQueue', 'button', + 'aiscript', ]; diff --git a/src/client/widgets/memo.vue b/src/client/widgets/memo.vue index dab19cd16e..3512429e0d 100644 --- a/src/client/widgets/memo.vue +++ b/src/client/widgets/memo.vue @@ -74,12 +74,18 @@ export default defineComponent({ max-width: 100%; min-width: 100%; padding: 16px; - color: var(--inputText); - background: var(--face); + color: var(--fg); + background: transparent; border: none; - border-bottom: solid var(--lineWidth) var(--faceDivider); + border-bottom: solid 1px var(--divider); border-radius: 0; box-sizing: border-box; + font: inherit; + font-size: 0.9em; + + &:focus { + outline: none; + } } > button { -- GitLab