diff options
Diffstat (limited to 'src/client')
24 files changed, 591 insertions, 339 deletions
diff --git a/src/client/components/page/page.block.vue b/src/client/components/page/page.block.vue index be933c8623..54b8b30276 100644 --- a/src/client/components/page/page.block.vue +++ b/src/client/components/page/page.block.vue @@ -1,9 +1,9 @@ <template> -<component :is="'x-' + value.type" :value="value" :page="page" :hpml="hpml" :key="value.id" :h="h"/> +<component :is="'x-' + block.type" :block="block" :hpml="hpml" :key="block.id" :h="h"/> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, PropType } from 'vue'; import XText from './page.text.vue'; import XSection from './page.section.vue'; import XImage from './page.image.vue'; @@ -19,22 +19,24 @@ import XCounter from './page.counter.vue'; import XRadioButton from './page.radio-button.vue'; import XCanvas from './page.canvas.vue'; import XNote from './page.note.vue'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { Block } from '@/scripts/hpml/block'; export default defineComponent({ components: { XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas, XNote }, props: { - value: { + block: { + type: Object as PropType<Block>, required: true }, hpml: { - required: true - }, - page: { + type: Object as PropType<Hpml>, required: true }, h: { + type: Number, required: true } }, diff --git a/src/client/components/page/page.button.vue b/src/client/components/page/page.button.vue index ff4c88554b..51da84bd49 100644 --- a/src/client/components/page/page.button.vue +++ b/src/client/components/page/page.button.vue @@ -1,51 +1,55 @@ <template> <div> - <MkButton class="kudkigyw" @click="click()" :primary="value.primary">{{ hpml.interpolate(value.text) }}</MkButton> + <MkButton class="kudkigyw" @click="click()" :primary="block.primary">{{ hpml.interpolate(block.text) }}</MkButton> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, PropType, unref } from 'vue'; import MkButton from '../ui/button.vue'; import * as os from '@/os'; +import { ButtonBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; export default defineComponent({ components: { MkButton }, props: { - value: { + block: { + type: Object as PropType<ButtonBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, methods: { click() { - if (this.value.action === 'dialog') { + if (this.block.action === 'dialog') { this.hpml.eval(); os.dialog({ - text: this.hpml.interpolate(this.value.content) + text: this.hpml.interpolate(this.block.content) }); - } else if (this.value.action === 'resetRandom') { + } else if (this.block.action === 'resetRandom') { this.hpml.updateRandomSeed(Math.random()); this.hpml.eval(); - } else if (this.value.action === 'pushEvent') { + } else if (this.block.action === 'pushEvent') { os.api('page-push', { pageId: this.hpml.page.id, - event: this.value.event, - ...(this.value.var ? { - var: this.hpml.vars[this.value.var] + event: this.block.event, + ...(this.block.var ? { + var: unref(this.hpml.vars)[this.block.var] } : {}) }); os.dialog({ type: 'success', - text: this.hpml.interpolate(this.value.message) + text: this.hpml.interpolate(this.block.message) }); - } else if (this.value.action === 'callAiScript') { - this.hpml.callAiScript(this.value.fn); + } else if (this.block.action === 'callAiScript') { + this.hpml.callAiScript(this.block.fn); } } } diff --git a/src/client/components/page/page.canvas.vue b/src/client/components/page/page.canvas.vue index d3bf5c1de6..8f49b88e5e 100644 --- a/src/client/components/page/page.canvas.vue +++ b/src/client/components/page/page.canvas.vue @@ -1,24 +1,36 @@ <template> <div class="ysrxegms"> - <canvas ref="canvas" :width="value.width" :height="value.height"/> + <canvas ref="canvas" :width="block.width" :height="block.height"/> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, onMounted, PropType, Ref, ref } from 'vue'; import * as os from '@/os'; +import { CanvasBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; export default defineComponent({ props: { - value: { + block: { + type: Object as PropType<CanvasBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - mounted() { - this.hpml.registerCanvas(this.value.name, this.$refs.canvas); + setup(props, ctx) { + const canvas: Ref<any> = ref(null); + + onMounted(() => { + props.hpml.registerCanvas(props.block.name, canvas.value); + }); + + return { + canvas + }; } }); </script> diff --git a/src/client/components/page/page.counter.vue b/src/client/components/page/page.counter.vue index 9eee47c4ec..b1af8954b0 100644 --- a/src/client/components/page/page.counter.vue +++ b/src/client/components/page/page.counter.vue @@ -1,41 +1,43 @@ <template> <div> - <MkButton class="llumlmnx" @click="click()">{{ hpml.interpolate(value.text) }}</MkButton> + <MkButton class="llumlmnx" @click="click()">{{ hpml.interpolate(block.text) }}</MkButton> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, PropType } from 'vue'; import MkButton from '../ui/button.vue'; import * as os from '@/os'; +import { CounterVarBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; export default defineComponent({ components: { MkButton }, props: { - value: { + block: { + type: Object as PropType<CounterVarBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - data() { + setup(props, ctx) { + const value = computed(() => { + return props.hpml.vars.value[props.block.name]; + }); + + function click() { + props.hpml.updatePageVar(props.block.name, value.value + (props.block.inc || 1)); + props.hpml.eval(); + } + return { - v: 0, + click }; - }, - watch: { - v() { - this.hpml.updatePageVar(this.value.name, this.v); - this.hpml.eval(); - } - }, - methods: { - click() { - this.v = this.v + (this.value.inc || 1); - } } }); </script> diff --git a/src/client/components/page/page.if.vue b/src/client/components/page/page.if.vue index 64081d5b5d..ec25332db0 100644 --- a/src/client/components/page/page.if.vue +++ b/src/client/components/page/page.if.vue @@ -1,27 +1,29 @@ <template> -<div v-show="hpml.vars[value.var]"> - <XBlock v-for="child in value.children" :value="child" :page="page" :hpml="hpml" :key="child.id" :h="h"/> +<div v-show="hpml.vars.value[block.var]"> + <XBlock v-for="child in block.children" :block="child" :hpml="hpml" :key="child.id" :h="h"/> </div> </template> <script lang="ts"> -import { defineComponent, defineAsyncComponent } from 'vue'; +import { IfBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { defineComponent, defineAsyncComponent, PropType } from 'vue'; export default defineComponent({ components: { XBlock: defineAsyncComponent(() => import('./page.block.vue')) }, props: { - value: { + block: { + type: Object as PropType<IfBlock>, required: true }, hpml: { - required: true - }, - page: { + type: Object as PropType<Hpml>, required: true }, h: { + type: Number, required: true } }, diff --git a/src/client/components/page/page.image.vue b/src/client/components/page/page.image.vue index ddcce5c37b..04ce74bd7c 100644 --- a/src/client/components/page/page.image.vue +++ b/src/client/components/page/page.image.vue @@ -5,25 +5,28 @@ </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, PropType } from 'vue'; import * as os from '@/os'; +import { ImageBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; export default defineComponent({ props: { - value: { + block: { + type: Object as PropType<ImageBlock>, required: true }, - page: { + hpml: { + type: Object as PropType<Hpml>, required: true - }, + } }, - data() { + setup(props, ctx) { + const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId); + return { - image: null, + image }; - }, - created() { - this.image = this.page.attachedFiles.find(x => x.id === this.value.fileId); } }); </script> diff --git a/src/client/components/page/page.note.vue b/src/client/components/page/page.note.vue index 27c1f1ed66..925844c1bd 100644 --- a/src/client/components/page/page.note.vue +++ b/src/client/components/page/page.note.vue @@ -1,15 +1,16 @@ <template> <div class="voxdxuby"> - <XNote v-if="note && !value.detailed" v-model:note="note" :key="note.id + ':normal'"/> - <XNoteDetailed v-if="note && value.detailed" v-model:note="note" :key="note.id + ':detail'"/> + <XNote v-if="note && !block.detailed" v-model:note="note" :key="note.id + ':normal'"/> + <XNoteDetailed v-if="note && block.detailed" v-model:note="note" :key="note.id + ':detail'"/> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, onMounted, PropType, Ref, ref } from 'vue'; import XNote from '@/components/note.vue'; import XNoteDetailed from '@/components/note-detailed.vue'; import * as os from '@/os'; +import { NoteBlock } from '@/scripts/hpml/block'; export default defineComponent({ components: { @@ -17,20 +18,24 @@ export default defineComponent({ XNoteDetailed, }, props: { - value: { - required: true - }, - hpml: { + block: { + type: Object as PropType<NoteBlock>, required: true } }, - data() { + setup(props, ctx) { + const note: Ref<Record<string, any> | null> = ref(null); + + onMounted(() => { + os.api('notes/show', { noteId: props.block.note }) + .then(result => { + note.value = result; + }); + }); + return { - note: null, + note }; - }, - async mounted() { - this.note = await os.api('notes/show', { noteId: this.value.note }); } }); </script> diff --git a/src/client/components/page/page.number-input.vue b/src/client/components/page/page.number-input.vue index cf4000010f..7b7d799330 100644 --- a/src/client/components/page/page.number-input.vue +++ b/src/client/components/page/page.number-input.vue @@ -1,36 +1,44 @@ <template> <div> - <MkInput class="kudkigyw" v-model:value="v" type="number">{{ hpml.interpolate(value.text) }}</MkInput> + <MkInput class="kudkigyw" :value="value" @update:value="updateValue($event)" type="number">{{ hpml.interpolate(block.text) }}</MkInput> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, PropType } from 'vue'; import MkInput from '../ui/input.vue'; import * as os from '@/os'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { NumberInputVarBlock } from '@/scripts/hpml/block'; export default defineComponent({ components: { MkInput }, props: { - value: { + block: { + type: Object as PropType<NumberInputVarBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - data() { + setup(props, ctx) { + const value = computed(() => { + return props.hpml.vars.value[props.block.name]; + }); + + function updateValue(newValue) { + props.hpml.updatePageVar(props.block.name, newValue); + props.hpml.eval(); + } + return { - v: this.value.default, + value, + updateValue }; - }, - watch: { - v() { - this.hpml.updatePageVar(this.value.name, this.v); - this.hpml.eval(); - } } }); </script> diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue index 46870ca0d7..33c6e6b14c 100644 --- a/src/client/components/page/page.post.vue +++ b/src/client/components/page/page.post.vue @@ -6,12 +6,14 @@ </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, PropType } from 'vue'; import { faCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons'; import MkTextarea from '../ui/textarea.vue'; import MkButton from '../ui/button.vue'; import { apiUrl } from '@/config'; import * as os from '@/os'; +import { PostBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; export default defineComponent({ components: { @@ -19,16 +21,18 @@ export default defineComponent({ MkButton, }, props: { - value: { + block: { + type: Object as PropType<PostBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, data() { return { - text: this.hpml.interpolate(this.value.text), + text: this.hpml.interpolate(this.block.text), posted: false, posting: false, faCheck, faPaperPlane @@ -37,7 +41,7 @@ export default defineComponent({ watch: { 'hpml.vars': { handler() { - this.text = this.hpml.interpolate(this.value.text); + this.text = this.hpml.interpolate(this.block.text); }, deep: true } @@ -45,7 +49,7 @@ export default defineComponent({ methods: { upload() { const promise = new Promise((ok) => { - const canvas = this.hpml.canvases[this.value.canvasId]; + const canvas = this.hpml.canvases[this.block.canvasId]; canvas.toBlob(blob => { const data = new FormData(); data.append('file', blob); @@ -69,7 +73,7 @@ export default defineComponent({ }, async post() { this.posting = true; - const file = this.value.attachCanvasImage ? await this.upload() : null; + const file = this.block.attachCanvasImage ? await this.upload() : null; os.apiWithDialog('notes/create', { text: this.text === '' ? null : this.text, fileIds: file ? [file.id] : undefined, diff --git a/src/client/components/page/page.radio-button.vue b/src/client/components/page/page.radio-button.vue index c4db5964cd..def6ee8885 100644 --- a/src/client/components/page/page.radio-button.vue +++ b/src/client/components/page/page.radio-button.vue @@ -1,37 +1,45 @@ <template> <div> - <div>{{ hpml.interpolate(value.title) }}</div> - <MkRadio v-for="x in value.values" v-model="v" :value="x" :key="x">{{ x }}</MkRadio> + <div>{{ hpml.interpolate(block.title) }}</div> + <MkRadio v-for="item in block.values" :modelValue="value" @update:modelValue="updateValue($event)" :value="item" :key="item">{{ item }}</MkRadio> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, PropType } from 'vue'; import MkRadio from '../ui/radio.vue'; import * as os from '@/os'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { RadioButtonVarBlock } from '@/scripts/hpml/block'; export default defineComponent({ components: { MkRadio }, props: { - value: { + block: { + type: Object as PropType<RadioButtonVarBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - data() { + setup(props, ctx) { + const value = computed(() => { + return props.hpml.vars.value[props.block.name]; + }); + + function updateValue(newValue: string) { + props.hpml.updatePageVar(props.block.name, newValue); + props.hpml.eval(); + } + return { - v: this.value.default, + value, + updateValue }; - }, - watch: { - v() { - this.hpml.updatePageVar(this.value.name, this.v); - this.hpml.eval(); - } } }); </script> diff --git a/src/client/components/page/page.section.vue b/src/client/components/page/page.section.vue index 9f05f3a0ce..d32f5dc732 100644 --- a/src/client/components/page/page.section.vue +++ b/src/client/components/page/page.section.vue @@ -1,29 +1,30 @@ <template> <section class="sdgxphyu"> - <component :is="'h' + h">{{ value.title }}</component> + <component :is="'h' + h">{{ block.title }}</component> <div class="children"> - <XBlock v-for="child in value.children" :value="child" :page="page" :hpml="hpml" :key="child.id" :h="h + 1"/> + <XBlock v-for="child in block.children" :block="child" :hpml="hpml" :key="child.id" :h="h + 1"/> </div> </section> </template> <script lang="ts"> -import { defineComponent, defineAsyncComponent } from 'vue'; +import { defineComponent, defineAsyncComponent, PropType } from 'vue'; import * as os from '@/os'; +import { SectionBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; export default defineComponent({ components: { XBlock: defineAsyncComponent(() => import('./page.block.vue')) }, props: { - value: { + block: { + type: Object as PropType<SectionBlock>, required: true }, hpml: { - required: true - }, - page: { + type: Object as PropType<Hpml>, required: true }, h: { diff --git a/src/client/components/page/page.switch.vue b/src/client/components/page/page.switch.vue index 33e4371695..9c3fb5feaa 100644 --- a/src/client/components/page/page.switch.vue +++ b/src/client/components/page/page.switch.vue @@ -1,36 +1,44 @@ <template> <div class="hkcxmtwj"> - <MkSwitch v-model:value="v">{{ hpml.interpolate(value.text) }}</MkSwitch> + <MkSwitch :value="value" @update:value="updateValue($event)">{{ hpml.interpolate(block.text) }}</MkSwitch> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, PropType } from 'vue'; import MkSwitch from '../ui/switch.vue'; import * as os from '@/os'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { SwitchVarBlock } from '@/scripts/hpml/block'; export default defineComponent({ components: { MkSwitch }, props: { - value: { + block: { + type: Object as PropType<SwitchVarBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - data() { + setup(props, ctx) { + const value = computed(() => { + return props.hpml.vars.value[props.block.name]; + }); + + function updateValue(newValue: boolean) { + props.hpml.updatePageVar(props.block.name, newValue); + props.hpml.eval(); + } + return { - v: this.value.default, + value, + updateValue }; - }, - watch: { - v() { - this.hpml.updatePageVar(this.value.name, this.v); - this.hpml.eval(); - } } }); </script> diff --git a/src/client/components/page/page.text-input.vue b/src/client/components/page/page.text-input.vue index f0fe70e335..e67814af16 100644 --- a/src/client/components/page/page.text-input.vue +++ b/src/client/components/page/page.text-input.vue @@ -1,36 +1,44 @@ <template> <div> - <MkInput class="kudkigyw" v-model:value="v" type="text">{{ hpml.interpolate(value.text) }}</MkInput> + <MkInput class="kudkigyw" :value="value" @update:value="updateValue($event)" type="text">{{ hpml.interpolate(block.text) }}</MkInput> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, PropType } from 'vue'; import MkInput from '../ui/input.vue'; import * as os from '@/os'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { TextInputVarBlock } from '@/scripts/hpml/block'; export default defineComponent({ components: { MkInput }, props: { - value: { + block: { + type: Object as PropType<TextInputVarBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - data() { + setup(props, ctx) { + const value = computed(() => { + return props.hpml.vars.value[props.block.name]; + }); + + function updateValue(newValue) { + props.hpml.updatePageVar(props.block.name, newValue); + props.hpml.eval(); + } + return { - v: this.value.default, + value, + updateValue }; - }, - watch: { - v() { - this.hpml.updatePageVar(this.value.name, this.v); - this.hpml.eval(); - } } }); </script> diff --git a/src/client/components/page/page.text.vue b/src/client/components/page/page.text.vue index f109c9f041..1896d00f42 100644 --- a/src/client/components/page/page.text.vue +++ b/src/client/components/page/page.text.vue @@ -6,7 +6,9 @@ </template> <script lang="ts"> -import { defineAsyncComponent, defineComponent } from 'vue'; +import { TextBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { defineAsyncComponent, defineComponent, PropType } from 'vue'; import { parse } from '../../../mfm/parse'; import { unique } from '../../../prelude/array'; @@ -15,16 +17,18 @@ export default defineComponent({ MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')), }, props: { - value: { + block: { + type: Object as PropType<TextBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, data() { return { - text: this.hpml.interpolate(this.value.text), + text: this.hpml.interpolate(this.block.text), }; }, computed: { @@ -43,7 +47,7 @@ export default defineComponent({ watch: { 'hpml.vars': { handler() { - this.text = this.hpml.interpolate(this.value.text); + this.text = this.hpml.interpolate(this.block.text); }, deep: true } diff --git a/src/client/components/page/page.textarea-input.vue b/src/client/components/page/page.textarea-input.vue index 9e3283aa08..085c2ba579 100644 --- a/src/client/components/page/page.textarea-input.vue +++ b/src/client/components/page/page.textarea-input.vue @@ -1,36 +1,45 @@ <template> <div> - <MkTextarea v-model:value="v">{{ hpml.interpolate(value.text) }}</MkTextarea> + <MkTextarea :value="value" @update:value="updateValue($event)">{{ hpml.interpolate(block.text) }}</MkTextarea> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, PropType } from 'vue'; import MkTextarea from '../ui/textarea.vue'; import * as os from '@/os'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { HpmlTextInput } from '@/scripts/hpml'; +import { TextInputVarBlock } from '@/scripts/hpml/block'; export default defineComponent({ components: { MkTextarea }, props: { - value: { + block: { + type: Object as PropType<TextInputVarBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, - data() { + setup(props, ctx) { + const value = computed(() => { + return props.hpml.vars.value[props.block.name]; + }); + + function updateValue(newValue) { + props.hpml.updatePageVar(props.block.name, newValue); + props.hpml.eval(); + } + return { - v: this.value.default, + value, + updateValue }; - }, - watch: { - v() { - this.hpml.updatePageVar(this.value.name, this.v); - this.hpml.eval(); - } } }); </script> diff --git a/src/client/components/page/page.textarea.vue b/src/client/components/page/page.textarea.vue index 205448977c..97d688368a 100644 --- a/src/client/components/page/page.textarea.vue +++ b/src/client/components/page/page.textarea.vue @@ -3,7 +3,9 @@ </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { TextBlock } from '@/scripts/hpml/block'; +import { Hpml } from '@/scripts/hpml/evaluator'; +import { defineComponent, PropType } from 'vue'; import MkTextarea from '../ui/textarea.vue'; export default defineComponent({ @@ -11,22 +13,24 @@ export default defineComponent({ MkTextarea }, props: { - value: { + block: { + type: Object as PropType<TextBlock>, required: true }, hpml: { + type: Object as PropType<Hpml>, required: true } }, data() { return { - text: this.hpml.interpolate(this.value.text), + text: this.hpml.interpolate(this.block.text), }; }, watch: { 'hpml.vars': { handler() { - this.text = this.hpml.interpolate(this.value.text); + this.text = this.hpml.interpolate(this.block.text); }, deep: true } diff --git a/src/client/components/page/page.vue b/src/client/components/page/page.vue index 1dee8e30be..6d1c419a40 100644 --- a/src/client/components/page/page.vue +++ b/src/client/components/page/page.vue @@ -1,77 +1,72 @@ <template> <div class="iroscrza" :class="{ center: page.alignCenter, serif: page.font === 'serif' }" v-if="hpml"> - <XBlock v-for="child in page.content" :value="child" :page="page" :hpml="hpml" :key="child.id" :h="2"/> + <XBlock v-for="child in page.content" :block="child" :hpml="hpml" :key="child.id" :h="2"/> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, onMounted, nextTick, onUnmounted, PropType } from 'vue'; import { parse } from '@syuilo/aiscript'; -import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons'; -import { faHeart } from '@fortawesome/free-regular-svg-icons'; import XBlock from './page.block.vue'; import { Hpml } from '@/scripts/hpml/evaluator'; import { url } from '@/config'; +import { $i } from '@/account'; +import { defaultStore } from '@/store'; export default defineComponent({ components: { XBlock }, - props: { page: { - type: Object, + type: Object as PropType<Record<string, any>>, required: true }, }, + setup(props, ctx) { - data() { - return { - hpml: null, - faHeartS, faHeart - }; - }, - - created() { - this.hpml = new Hpml(this.page, { + const hpml = new Hpml(props.page, { randomSeed: Math.random(), - visitor: this.$i, + visitor: $i, url: url, - enableAiScript: !this.$store.state.disablePagesScript + enableAiScript: !defaultStore.state.disablePagesScript }); - }, - mounted() { - this.$nextTick(() => { - if (this.page.script && this.hpml.aiscript) { - let ast; - try { - ast = parse(this.page.script); - } catch (e) { - console.error(e); - /*os.dialog({ - type: 'error', - text: 'Syntax error :(' - });*/ - return; + onMounted(() => { + nextTick(() => { + if (props.page.script && hpml.aiscript) { + let ast; + try { + ast = parse(props.page.script); + } catch (e) { + console.error(e); + /*os.dialog({ + type: 'error', + text: 'Syntax error :(' + });*/ + return; + } + hpml.aiscript.exec(ast).then(() => { + hpml.eval(); + }).catch(e => { + console.error(e); + /*os.dialog({ + type: 'error', + text: e + });*/ + }); + } else { + hpml.eval(); } - this.hpml.aiscript.exec(ast).then(() => { - this.hpml.eval(); - }).catch(e => { - console.error(e); - /*os.dialog({ - type: 'error', - text: e - });*/ - }); - } else { - this.hpml.eval(); - } + }); + onUnmounted(() => { + if (hpml.aiscript) hpml.aiscript.abort(); + }); }); - }, - beforeUnmount() { - if (this.hpml.aiscript) this.hpml.aiscript.abort(); + return { + hpml, + }; }, }); </script> diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue index 3fbcd1b19b..68bc1b50c1 100644 --- a/src/client/pages/page-editor/page-editor.script-block.vue +++ b/src/client/pages/page-editor/page-editor.script-block.vue @@ -61,8 +61,10 @@ import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons'; import { v4 as uuid } from 'uuid'; import XContainer from './page-editor.container.vue'; import MkTextarea from '@/components/ui/textarea.vue'; -import { isLiteralBlock, funcDefs, blockDefs } from '@/scripts/hpml/index'; +import { blockDefs } from '@/scripts/hpml/index'; import * as os from '@/os'; +import { isLiteralValue } from '@/scripts/hpml/expr'; +import { funcDefs } from '@/scripts/hpml/lib'; export default defineComponent({ components: { @@ -166,7 +168,7 @@ export default defineComponent({ return; } - if (isLiteralBlock(this.value)) return; + if (isLiteralValue(this.value)) return; const empties = []; for (let i = 0; i < funcDefs[this.value.type].in.length; i++) { diff --git a/src/client/scripts/hpml/block.ts b/src/client/scripts/hpml/block.ts new file mode 100644 index 0000000000..804c5c1124 --- /dev/null +++ b/src/client/scripts/hpml/block.ts @@ -0,0 +1,109 @@ +// blocks + +export type BlockBase = { + id: string; + type: string; +}; + +export type TextBlock = BlockBase & { + type: 'text'; + text: string; +}; + +export type SectionBlock = BlockBase & { + type: 'section'; + title: string; + children: (Block | VarBlock)[]; +}; + +export type ImageBlock = BlockBase & { + type: 'image'; + fileId: string | null; +}; + +export type ButtonBlock = BlockBase & { + type: 'button'; + text: any; + primary: boolean; + action: string; + content: string; + event: string; + message: string; + var: string; + fn: string; +}; + +export type IfBlock = BlockBase & { + type: 'if'; + var: string; + children: Block[]; +}; + +export type TextareaBlock = BlockBase & { + type: 'textarea'; + text: string; +}; + +export type PostBlock = BlockBase & { + type: 'post'; + text: string; + attachCanvasImage: boolean; + canvasId: string; +}; + +export type CanvasBlock = BlockBase & { + type: 'canvas'; + name: string; // canvas id + width: number; + height: number; +}; + +export type NoteBlock = BlockBase & { + type: 'note'; + detailed: boolean; + note: string | null; +}; + +export type Block = + TextBlock | SectionBlock | ImageBlock | ButtonBlock | IfBlock | TextareaBlock | PostBlock | CanvasBlock | NoteBlock | VarBlock; + +// variable blocks + +export type VarBlockBase = BlockBase & { + name: string; +}; + +export type NumberInputVarBlock = VarBlockBase & { + type: 'numberInput'; + text: string; +}; + +export type TextInputVarBlock = VarBlockBase & { + type: 'textInput'; + text: string; +}; + +export type SwitchVarBlock = VarBlockBase & { + type: 'switch'; + text: string; +}; + +export type RadioButtonVarBlock = VarBlockBase & { + type: 'radioButton'; + title: string; + values: string[]; +}; + +export type CounterVarBlock = VarBlockBase & { + type: 'counter'; + text: string; + inc: number; +}; + +export type VarBlock = + NumberInputVarBlock | TextInputVarBlock | SwitchVarBlock | RadioButtonVarBlock | CounterVarBlock; + +const varBlock = ['numberInput', 'textInput', 'switch', 'radioButton', 'counter']; +export function isVarBlock(block: Block): block is VarBlock { + return varBlock.includes(block.type); +} diff --git a/src/client/scripts/hpml/evaluator.ts b/src/client/scripts/hpml/evaluator.ts index 4fa95e89c9..20261d333d 100644 --- a/src/client/scripts/hpml/evaluator.ts +++ b/src/client/scripts/hpml/evaluator.ts @@ -1,12 +1,13 @@ import autobind from 'autobind-decorator'; -import { Variable, PageVar, envVarsDef, Block, isFnBlock, Fn, HpmlScope, HpmlError } from '.'; +import { PageVar, envVarsDef, Fn, HpmlScope, HpmlError } from '.'; import { version } from '@/config'; import { AiScript, utils, values } from '@syuilo/aiscript'; import { createAiScriptEnv } from '../aiscript/api'; import { collectPageVars } from '../collect-page-vars'; import { initHpmlLib, initAiLib } from './lib'; import * as os from '@/os'; -import { markRaw, ref, Ref } from 'vue'; +import { markRaw, ref, Ref, unref } from 'vue'; +import { Expr, isLiteralValue, Variable } from './expr'; /** * Hpml evaluator @@ -94,7 +95,7 @@ export class Hpml { public interpolate(str: string) { if (str == null) return null; return str.replace(/{(.+?)}/g, match => { - const v = this.vars[match.slice(1, -1).trim()]; + const v = unref(this.vars)[match.slice(1, -1).trim()]; return v == null ? 'NULL' : v.toString(); }); } @@ -158,72 +159,76 @@ export class Hpml { } @autobind - private evaluate(block: Block, scope: HpmlScope): any { - if (block.type === null) { - return null; - } + private evaluate(expr: Expr, scope: HpmlScope): any { - if (block.type === 'number') { - return parseInt(block.value, 10); - } + if (isLiteralValue(expr)) { + if (expr.type === null) { + return null; + } - if (block.type === 'text' || block.type === 'multiLineText') { - return this._interpolateScope(block.value || '', scope); - } + if (expr.type === 'number') { + return parseInt((expr.value as any), 10); + } - if (block.type === 'textList') { - return this._interpolateScope(block.value || '', scope).trim().split('\n'); - } + if (expr.type === 'text' || expr.type === 'multiLineText') { + return this._interpolateScope(expr.value || '', scope); + } - if (block.type === 'ref') { - return scope.getState(block.value); - } + if (expr.type === 'textList') { + return this._interpolateScope(expr.value || '', scope).trim().split('\n'); + } + + if (expr.type === 'ref') { + return scope.getState(expr.value); + } - if (block.type === 'aiScriptVar') { - if (this.aiscript) { - try { - return utils.valToJs(this.aiscript.scope.get(block.value)); - } catch (e) { + if (expr.type === 'aiScriptVar') { + if (this.aiscript) { + try { + return utils.valToJs(this.aiscript.scope.get(expr.value)); + } catch (e) { + return null; + } + } else { return null; } - } else { - return null; } - } - // Define user function - if (isFnBlock(block)) { - return { - slots: block.value.slots.map(x => x.name), - exec: (slotArg: Record<string, any>) => { - return this.evaluate(block.value.expression, scope.createChildScope(slotArg, block.id)); - } - } as Fn; + // Define user function + if (expr.type == 'fn') { + return { + slots: expr.value.slots.map(x => x.name), + exec: (slotArg: Record<string, any>) => { + return this.evaluate(expr.value.expression, scope.createChildScope(slotArg, expr.id)); + } + } as Fn; + } + return; } // Call user function - if (block.type.startsWith('fn:')) { - const fnName = block.type.split(':')[1]; + if (expr.type.startsWith('fn:')) { + const fnName = expr.type.split(':')[1]; const fn = scope.getState(fnName); const args = {} as Record<string, any>; for (let i = 0; i < fn.slots.length; i++) { const name = fn.slots[i]; - args[name] = this.evaluate(block.args[i], scope); + args[name] = this.evaluate(expr.args[i], scope); } return fn.exec(args); } - if (block.args === undefined) return null; + if (expr.args === undefined) return null; - const funcs = initHpmlLib(block, scope, this.opts.randomSeed, this.opts.visitor); + const funcs = initHpmlLib(expr, scope, this.opts.randomSeed, this.opts.visitor); // Call function - const fnName = block.type; + const fnName = expr.type; const fn = (funcs as any)[fnName]; if (fn == null) { throw new HpmlError(`No such function '${fnName}'`); } else { - return fn(...block.args.map(x => this.evaluate(x, scope))); + return fn(...expr.args.map(x => this.evaluate(x, scope))); } } } diff --git a/src/client/scripts/hpml/expr.ts b/src/client/scripts/hpml/expr.ts new file mode 100644 index 0000000000..00e3ed118b --- /dev/null +++ b/src/client/scripts/hpml/expr.ts @@ -0,0 +1,79 @@ +import { literalDefs, Type } from '.'; + +export type ExprBase = { + id: string; +}; + +// value + +export type EmptyValue = ExprBase & { + type: null; + value: null; +}; + +export type TextValue = ExprBase & { + type: 'text'; + value: string; +}; + +export type MultiLineTextValue = ExprBase & { + type: 'multiLineText'; + value: string; +}; + +export type TextListValue = ExprBase & { + type: 'textList'; + value: string; +}; + +export type NumberValue = ExprBase & { + type: 'number'; + value: number; +}; + +export type RefValue = ExprBase & { + type: 'ref'; + value: string; // value is variable name +}; + +export type AiScriptRefValue = ExprBase & { + type: 'aiScriptVar'; + value: string; // value is variable name +}; + +export type UserFnValue = ExprBase & { + type: 'fn'; + value: UserFnInnerValue; +}; +type UserFnInnerValue = { + slots: { + name: string; + type: Type; + }[]; + expression: Expr; +}; + +export type Value = + EmptyValue | TextValue | MultiLineTextValue | TextListValue | NumberValue | RefValue | AiScriptRefValue | UserFnValue; + +export function isLiteralValue(expr: Expr): expr is Value { + if (expr.type == null) return true; + if (literalDefs[expr.type]) return true; + return false; +} + +// call function + +export type CallFn = ExprBase & { // "fn:hoge" or string + type: string; + args: Expr[]; + value: null; +}; + +// variable +export type Variable = (Value | CallFn) & { + name: string; +}; + +// expression +export type Expr = Variable | Value | CallFn; diff --git a/src/client/scripts/hpml/index.ts b/src/client/scripts/hpml/index.ts index fa34b25d8d..924cd32eb5 100644 --- a/src/client/scripts/hpml/index.ts +++ b/src/client/scripts/hpml/index.ts @@ -3,52 +3,16 @@ */ import autobind from 'autobind-decorator'; - import { faMagic, faSquareRootAlt, faAlignLeft, - faShareAlt, - faPlus, - faMinus, - faTimes, - faDivide, faList, faQuoteRight, - faEquals, - faGreaterThan, - faLessThan, - faGreaterThanEqual, - faLessThanEqual, - faNotEqual, - faDice, faSortNumericUp, - faExchangeAlt, - faRecycle, - faIndent, - faCalculator, } from '@fortawesome/free-solid-svg-icons'; -import { faFlag } from '@fortawesome/free-regular-svg-icons'; import { Hpml } from './evaluator'; - -export type Block<V = any> = { - id: string; - type: string; - args: Block[]; - value: V; -}; - -export type FnBlock = Block<{ - slots: { - name: string; - type: Type; - }[]; - expression: Block; -}>; - -export type Variable = Block & { - name: string; -}; +import { funcDefs } from './lib'; export type Fn = { slots: string[]; @@ -57,46 +21,6 @@ export type Fn = { export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null; -export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = { - if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: faShareAlt, }, - for: { in: ['number', 'function'], out: null, category: 'flow', icon: faRecycle, }, - not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, - or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, - and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, - add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faPlus, }, - subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, }, - multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, }, - divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, }, - mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, }, - round: { in: ['number'], out: 'number', category: 'operation', icon: faCalculator, }, - eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, }, - notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, }, - gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, }, - lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThan, }, - gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThanEqual, }, - ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThanEqual, }, - strLen: { in: ['string'], out: 'number', category: 'text', icon: faQuoteRight, }, - strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: faQuoteRight, }, - strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: faQuoteRight, }, - strReverse: { in: ['string'], out: 'string', category: 'text', icon: faQuoteRight, }, - join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: faQuoteRight, }, - stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: faExchangeAlt, }, - numberToString: { in: ['number'], out: 'string', category: 'convert', icon: faExchangeAlt, }, - splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: faExchangeAlt, }, - pick: { in: [null, 'number'], out: null, category: 'list', icon: faIndent, }, - listLen: { in: [null], out: 'number', category: 'list', icon: faIndent, }, - rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, }, - dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, }, - seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: faDice, }, - random: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, }, - dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, }, - seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: faDice, }, - randomPick: { in: [0], out: 0, category: 'random', icon: faDice, }, - dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, }, - seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: faDice, }, - DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: faDice, }, // dailyRandomPickWithProbabilityMapping -}; - export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = { text: { out: 'string', category: 'value', icon: faQuoteRight, }, multiLineText: { out: 'string', category: 'value', icon: faAlignLeft, }, @@ -116,10 +40,6 @@ export const blockDefs = [ })) ]; -export function isFnBlock(block: Block): block is FnBlock { - return block.type === 'fn'; -} - export type PageVar = { name: string; value: any; type: Type; }; export const envVarsDef: Record<string, Type> = { @@ -140,12 +60,6 @@ export const envVarsDef: Record<string, Type> = { NULL: null, }; -export function isLiteralBlock(v: Block) { - if (v.type === null) return true; - if (literalDefs[v.type]) return true; - return false; -} - export class HpmlScope { private layerdStates: Record<string, any>[]; public name: string; diff --git a/src/client/scripts/hpml/lib.ts b/src/client/scripts/hpml/lib.ts index 11e4f2fc39..7454562184 100644 --- a/src/client/scripts/hpml/lib.ts +++ b/src/client/scripts/hpml/lib.ts @@ -2,9 +2,31 @@ import * as tinycolor from 'tinycolor2'; import Chart from 'chart.js'; import { Hpml } from './evaluator'; import { values, utils } from '@syuilo/aiscript'; -import { Block, Fn, HpmlScope } from '.'; +import { Fn, HpmlScope } from '.'; +import { Expr } from './expr'; import * as seedrandom from 'seedrandom'; +import { + faShareAlt, + faPlus, + faMinus, + faTimes, + faDivide, + faQuoteRight, + faEquals, + faGreaterThan, + faLessThan, + faGreaterThanEqual, + faLessThanEqual, + faNotEqual, + faDice, + faExchangeAlt, + faRecycle, + faIndent, + faCalculator, +} from '@fortawesome/free-solid-svg-icons'; +import { faFlag } from '@fortawesome/free-regular-svg-icons'; + // https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs Chart.pluginService.register({ beforeDraw: (chart, easing) => { @@ -125,7 +147,47 @@ export function initAiLib(hpml: Hpml) { }; } -export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string, visitor?: any) { +export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = { + if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: faShareAlt, }, + for: { in: ['number', 'function'], out: null, category: 'flow', icon: faRecycle, }, + not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, + or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, + and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, }, + add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faPlus, }, + subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, }, + multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, }, + divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, }, + mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, }, + round: { in: ['number'], out: 'number', category: 'operation', icon: faCalculator, }, + eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, }, + notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, }, + gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, }, + lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThan, }, + gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThanEqual, }, + ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThanEqual, }, + strLen: { in: ['string'], out: 'number', category: 'text', icon: faQuoteRight, }, + strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: faQuoteRight, }, + strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: faQuoteRight, }, + strReverse: { in: ['string'], out: 'string', category: 'text', icon: faQuoteRight, }, + join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: faQuoteRight, }, + stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: faExchangeAlt, }, + numberToString: { in: ['number'], out: 'string', category: 'convert', icon: faExchangeAlt, }, + splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: faExchangeAlt, }, + pick: { in: [null, 'number'], out: null, category: 'list', icon: faIndent, }, + listLen: { in: [null], out: 'number', category: 'list', icon: faIndent, }, + rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, }, + dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, }, + seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: faDice, }, + random: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, }, + dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, }, + seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: faDice, }, + randomPick: { in: [0], out: 0, category: 'random', icon: faDice, }, + dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, }, + seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: faDice, }, + DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: faDice, }, // dailyRandomPickWithProbabilityMapping +}; + +export function initHpmlLib(expr: Expr, scope: HpmlScope, randomSeed: string, visitor?: any) { const date = new Date(); const day = `${visitor ? visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; @@ -166,12 +228,12 @@ export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string, splitStrByLine: (a: string) => a.split('\n'), pick: (list: any[], i: number) => list[i - 1], listLen: (list: any[]) => list.length, - random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * 100) < probability, - rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * (max - min + 1)), - randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * list.length)], - dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability, - dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)), - dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)], + random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * 100) < probability, + rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * (max - min + 1)), + randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * list.length)], + dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${expr.id}`)() * 100) < probability, + dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${expr.id}`)() * (max - min + 1)), + dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${expr.id}`)() * list.length)], seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability, seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)), seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)], @@ -185,7 +247,7 @@ export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string, totalFactor += factor; xs.push({ factor, text }); } - const r = seedrandom(`${day}:${block.id}`)() * totalFactor; + const r = seedrandom(`${day}:${expr.id}`)() * totalFactor; let stackedFactor = 0; for (const x of xs) { if (r >= stackedFactor && r <= stackedFactor + x.factor) { diff --git a/src/client/scripts/hpml/type-checker.ts b/src/client/scripts/hpml/type-checker.ts index 14950e0195..9633b3cd01 100644 --- a/src/client/scripts/hpml/type-checker.ts +++ b/src/client/scripts/hpml/type-checker.ts @@ -1,5 +1,7 @@ import autobind from 'autobind-decorator'; -import { Type, Block, funcDefs, envVarsDef, Variable, PageVar, isLiteralBlock } from '.'; +import { Type, envVarsDef, PageVar } from '.'; +import { Expr, isLiteralValue, Variable } from './expr'; +import { funcDefs } from './lib'; type TypeError = { arg: number; @@ -20,10 +22,10 @@ export class HpmlTypeChecker { } @autobind - public typeCheck(v: Block): TypeError | null { - if (isLiteralBlock(v)) return null; + public typeCheck(v: Expr): TypeError | null { + if (isLiteralValue(v)) return null; - const def = funcDefs[v.type]; + const def = funcDefs[v.type || '']; if (def == null) { throw new Error('Unknown type: ' + v.type); } @@ -58,8 +60,8 @@ export class HpmlTypeChecker { } @autobind - public getExpectedType(v: Block, slot: number): Type { - const def = funcDefs[v.type]; + public getExpectedType(v: Expr, slot: number): Type { + const def = funcDefs[v.type || '']; if (def == null) { throw new Error('Unknown type: ' + v.type); } @@ -86,7 +88,7 @@ export class HpmlTypeChecker { } @autobind - public infer(v: Block): Type { + public infer(v: Expr): Type { if (v.type === null) return null; if (v.type === 'text') return 'string'; if (v.type === 'multiLineText') return 'string'; @@ -103,7 +105,7 @@ export class HpmlTypeChecker { return pageVar.type; } - const envVar = envVarsDef[v.value]; + const envVar = envVarsDef[v.value || '']; if (envVar !== undefined) { return envVar; } |