mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-14 06:23:08 +02:00
pages refactoring, fix bug (#7066)
* pages refactoring * pages: fix if block * fix code format * remove passing of the page parameter * remove comment * fix indent * replace with unref * fix conditions of isVarBlock() * Update src/client/scripts/hpml/block.ts use includes() instead of find() Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
parent
7fc3e7dd8b
commit
100a131913
24 changed files with 600 additions and 348 deletions
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import XText from './page.text.vue';
|
import XText from './page.text.vue';
|
||||||
import XSection from './page.section.vue';
|
import XSection from './page.section.vue';
|
||||||
import XImage from './page.image.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 XRadioButton from './page.radio-button.vue';
|
||||||
import XCanvas from './page.canvas.vue';
|
import XCanvas from './page.canvas.vue';
|
||||||
import XNote from './page.note.vue';
|
import XNote from './page.note.vue';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
import { Block } from '@/scripts/hpml/block';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas, XNote
|
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas, XNote
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<Block>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
required: true
|
type: Object as PropType<Hpml>,
|
||||||
},
|
|
||||||
page: {
|
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
h: {
|
h: {
|
||||||
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,51 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, PropType, unref } from 'vue';
|
||||||
import MkButton from '../ui/button.vue';
|
import MkButton from '../ui/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { ButtonBlock } from '@/scripts/hpml/block';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkButton
|
MkButton
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<ButtonBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
click() {
|
click() {
|
||||||
if (this.value.action === 'dialog') {
|
if (this.block.action === 'dialog') {
|
||||||
this.hpml.eval();
|
this.hpml.eval();
|
||||||
os.dialog({
|
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.updateRandomSeed(Math.random());
|
||||||
this.hpml.eval();
|
this.hpml.eval();
|
||||||
} else if (this.value.action === 'pushEvent') {
|
} else if (this.block.action === 'pushEvent') {
|
||||||
os.api('page-push', {
|
os.api('page-push', {
|
||||||
pageId: this.hpml.page.id,
|
pageId: this.hpml.page.id,
|
||||||
event: this.value.event,
|
event: this.block.event,
|
||||||
...(this.value.var ? {
|
...(this.block.var ? {
|
||||||
var: this.hpml.vars[this.value.var]
|
var: unref(this.hpml.vars)[this.block.var]
|
||||||
} : {})
|
} : {})
|
||||||
});
|
});
|
||||||
|
|
||||||
os.dialog({
|
os.dialog({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
text: this.hpml.interpolate(this.value.message)
|
text: this.hpml.interpolate(this.block.message)
|
||||||
});
|
});
|
||||||
} else if (this.value.action === 'callAiScript') {
|
} else if (this.block.action === 'callAiScript') {
|
||||||
this.hpml.callAiScript(this.value.fn);
|
this.hpml.callAiScript(this.block.fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,36 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ysrxegms">
|
<div class="ysrxegms">
|
||||||
<canvas ref="canvas" :width="value.width" :height="value.height"/>
|
<canvas ref="canvas" :width="block.width" :height="block.height"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, onMounted, PropType, Ref, ref } from 'vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { CanvasBlock } from '@/scripts/hpml/block';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<CanvasBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
setup(props, ctx) {
|
||||||
this.hpml.registerCanvas(this.value.name, this.$refs.canvas);
|
const canvas: Ref<any> = ref(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
props.hpml.registerCanvas(props.block.name, canvas.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
canvas
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,41 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<MkButton class="llumlmnx" @click="click()">{{ hpml.interpolate(value.text) }}</MkButton>
|
<MkButton class="llumlmnx" @click="click()">{{ hpml.interpolate(block.text) }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
import MkButton from '../ui/button.vue';
|
import MkButton from '../ui/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { CounterVarBlock } from '@/scripts/hpml/block';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkButton
|
MkButton
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<CounterVarBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
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 {
|
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>
|
</script>
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="hpml.vars[value.var]">
|
<div v-show="hpml.vars.value[block.var]">
|
||||||
<XBlock v-for="child in value.children" :value="child" :page="page" :hpml="hpml" :key="child.id" :h="h"/>
|
<XBlock v-for="child in block.children" :block="child" :hpml="hpml" :key="child.id" :h="h"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
XBlock: defineAsyncComponent(() => import('./page.block.vue'))
|
XBlock: defineAsyncComponent(() => import('./page.block.vue'))
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<IfBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
required: true
|
type: Object as PropType<Hpml>,
|
||||||
},
|
|
||||||
page: {
|
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
h: {
|
h: {
|
||||||
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,25 +5,28 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { ImageBlock } from '@/scripts/hpml/block';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<ImageBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
page: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
|
const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
image: null,
|
image
|
||||||
};
|
};
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.image = this.page.attachedFiles.find(x => x.id === this.value.fileId);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="voxdxuby">
|
<div class="voxdxuby">
|
||||||
<XNote v-if="note && !value.detailed" v-model:note="note" :key="note.id + ':normal'"/>
|
<XNote v-if="note && !block.detailed" v-model:note="note" :key="note.id + ':normal'"/>
|
||||||
<XNoteDetailed v-if="note && value.detailed" v-model:note="note" :key="note.id + ':detail'"/>
|
<XNoteDetailed v-if="note && block.detailed" v-model:note="note" :key="note.id + ':detail'"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, onMounted, PropType, Ref, ref } from 'vue';
|
||||||
import XNote from '@/components/note.vue';
|
import XNote from '@/components/note.vue';
|
||||||
import XNoteDetailed from '@/components/note-detailed.vue';
|
import XNoteDetailed from '@/components/note-detailed.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { NoteBlock } from '@/scripts/hpml/block';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -17,20 +18,24 @@ export default defineComponent({
|
||||||
XNoteDetailed,
|
XNoteDetailed,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
required: true
|
type: Object as PropType<NoteBlock>,
|
||||||
},
|
|
||||||
hpml: {
|
|
||||||
required: true
|
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 {
|
return {
|
||||||
note: null,
|
note
|
||||||
};
|
};
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
this.note = await os.api('notes/show', { noteId: this.value.note });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,36 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
import MkInput from '../ui/input.vue';
|
import MkInput from '../ui/input.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
import { NumberInputVarBlock } from '@/scripts/hpml/block';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkInput
|
MkInput
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<NumberInputVarBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
return {
|
const value = computed(() => {
|
||||||
v: this.value.default,
|
return props.hpml.vars.value[props.block.name];
|
||||||
};
|
});
|
||||||
},
|
|
||||||
watch: {
|
function updateValue(newValue) {
|
||||||
v() {
|
props.hpml.updatePageVar(props.block.name, newValue);
|
||||||
this.hpml.updatePageVar(this.value.name, this.v);
|
props.hpml.eval();
|
||||||
this.hpml.eval();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
updateValue
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import { faCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons';
|
import { faCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons';
|
||||||
import MkTextarea from '../ui/textarea.vue';
|
import MkTextarea from '../ui/textarea.vue';
|
||||||
import MkButton from '../ui/button.vue';
|
import MkButton from '../ui/button.vue';
|
||||||
import { apiUrl } from '@/config';
|
import { apiUrl } from '@/config';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { PostBlock } from '@/scripts/hpml/block';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -19,16 +21,18 @@ export default defineComponent({
|
||||||
MkButton,
|
MkButton,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<PostBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
text: this.hpml.interpolate(this.value.text),
|
text: this.hpml.interpolate(this.block.text),
|
||||||
posted: false,
|
posted: false,
|
||||||
posting: false,
|
posting: false,
|
||||||
faCheck, faPaperPlane
|
faCheck, faPaperPlane
|
||||||
|
@ -37,7 +41,7 @@ export default defineComponent({
|
||||||
watch: {
|
watch: {
|
||||||
'hpml.vars': {
|
'hpml.vars': {
|
||||||
handler() {
|
handler() {
|
||||||
this.text = this.hpml.interpolate(this.value.text);
|
this.text = this.hpml.interpolate(this.block.text);
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
|
@ -45,7 +49,7 @@ export default defineComponent({
|
||||||
methods: {
|
methods: {
|
||||||
upload() {
|
upload() {
|
||||||
const promise = new Promise((ok) => {
|
const promise = new Promise((ok) => {
|
||||||
const canvas = this.hpml.canvases[this.value.canvasId];
|
const canvas = this.hpml.canvases[this.block.canvasId];
|
||||||
canvas.toBlob(blob => {
|
canvas.toBlob(blob => {
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
data.append('file', blob);
|
data.append('file', blob);
|
||||||
|
@ -69,7 +73,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
async post() {
|
async post() {
|
||||||
this.posting = true;
|
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', {
|
os.apiWithDialog('notes/create', {
|
||||||
text: this.text === '' ? null : this.text,
|
text: this.text === '' ? null : this.text,
|
||||||
fileIds: file ? [file.id] : undefined,
|
fileIds: file ? [file.id] : undefined,
|
||||||
|
|
|
@ -1,37 +1,45 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div>{{ hpml.interpolate(value.title) }}</div>
|
<div>{{ hpml.interpolate(block.title) }}</div>
|
||||||
<MkRadio v-for="x in value.values" v-model="v" :value="x" :key="x">{{ x }}</MkRadio>
|
<MkRadio v-for="item in block.values" :modelValue="value" @update:modelValue="updateValue($event)" :value="item" :key="item">{{ item }}</MkRadio>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
import MkRadio from '../ui/radio.vue';
|
import MkRadio from '../ui/radio.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
import { RadioButtonVarBlock } from '@/scripts/hpml/block';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkRadio
|
MkRadio
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<RadioButtonVarBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
return {
|
const value = computed(() => {
|
||||||
v: this.value.default,
|
return props.hpml.vars.value[props.block.name];
|
||||||
};
|
});
|
||||||
},
|
|
||||||
watch: {
|
function updateValue(newValue: string) {
|
||||||
v() {
|
props.hpml.updatePageVar(props.block.name, newValue);
|
||||||
this.hpml.updatePageVar(this.value.name, this.v);
|
props.hpml.eval();
|
||||||
this.hpml.eval();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
updateValue
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="sdgxphyu">
|
<section class="sdgxphyu">
|
||||||
<component :is="'h' + h">{{ value.title }}</component>
|
<component :is="'h' + h">{{ block.title }}</component>
|
||||||
|
|
||||||
<div class="children">
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, defineAsyncComponent } from 'vue';
|
import { defineComponent, defineAsyncComponent, PropType } from 'vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { SectionBlock } from '@/scripts/hpml/block';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
XBlock: defineAsyncComponent(() => import('./page.block.vue'))
|
XBlock: defineAsyncComponent(() => import('./page.block.vue'))
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<SectionBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
required: true
|
type: Object as PropType<Hpml>,
|
||||||
},
|
|
||||||
page: {
|
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
h: {
|
h: {
|
||||||
|
|
|
@ -1,36 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="hkcxmtwj">
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
import MkSwitch from '../ui/switch.vue';
|
import MkSwitch from '../ui/switch.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
import { SwitchVarBlock } from '@/scripts/hpml/block';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkSwitch
|
MkSwitch
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<SwitchVarBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
return {
|
const value = computed(() => {
|
||||||
v: this.value.default,
|
return props.hpml.vars.value[props.block.name];
|
||||||
};
|
});
|
||||||
},
|
|
||||||
watch: {
|
function updateValue(newValue: boolean) {
|
||||||
v() {
|
props.hpml.updatePageVar(props.block.name, newValue);
|
||||||
this.hpml.updatePageVar(this.value.name, this.v);
|
props.hpml.eval();
|
||||||
this.hpml.eval();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
updateValue
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,36 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
import MkInput from '../ui/input.vue';
|
import MkInput from '../ui/input.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
|
import { TextInputVarBlock } from '@/scripts/hpml/block';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkInput
|
MkInput
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<TextInputVarBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
return {
|
const value = computed(() => {
|
||||||
v: this.value.default,
|
return props.hpml.vars.value[props.block.name];
|
||||||
};
|
});
|
||||||
},
|
|
||||||
watch: {
|
function updateValue(newValue) {
|
||||||
v() {
|
props.hpml.updatePageVar(props.block.name, newValue);
|
||||||
this.hpml.updatePageVar(this.value.name, this.v);
|
props.hpml.eval();
|
||||||
this.hpml.eval();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
updateValue
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 { parse } from '../../../mfm/parse';
|
||||||
import { unique } from '../../../prelude/array';
|
import { unique } from '../../../prelude/array';
|
||||||
|
|
||||||
|
@ -15,16 +17,18 @@ export default defineComponent({
|
||||||
MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
|
MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<TextBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
text: this.hpml.interpolate(this.value.text),
|
text: this.hpml.interpolate(this.block.text),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -43,7 +47,7 @@ export default defineComponent({
|
||||||
watch: {
|
watch: {
|
||||||
'hpml.vars': {
|
'hpml.vars': {
|
||||||
handler() {
|
handler() {
|
||||||
this.text = this.hpml.interpolate(this.value.text);
|
this.text = this.hpml.interpolate(this.block.text);
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,45 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<MkTextarea v-model:value="v">{{ hpml.interpolate(value.text) }}</MkTextarea>
|
<MkTextarea :value="value" @update:value="updateValue($event)">{{ hpml.interpolate(block.text) }}</MkTextarea>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
import MkTextarea from '../ui/textarea.vue';
|
import MkTextarea from '../ui/textarea.vue';
|
||||||
import * as os from '@/os';
|
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({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkTextarea
|
MkTextarea
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<TextInputVarBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
return {
|
const value = computed(() => {
|
||||||
v: this.value.default,
|
return props.hpml.vars.value[props.block.name];
|
||||||
};
|
});
|
||||||
},
|
|
||||||
watch: {
|
function updateValue(newValue) {
|
||||||
v() {
|
props.hpml.updatePageVar(props.block.name, newValue);
|
||||||
this.hpml.updatePageVar(this.value.name, this.v);
|
props.hpml.eval();
|
||||||
this.hpml.eval();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
updateValue
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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';
|
import MkTextarea from '../ui/textarea.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -11,22 +13,24 @@ export default defineComponent({
|
||||||
MkTextarea
|
MkTextarea
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
block: {
|
||||||
|
type: Object as PropType<TextBlock>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
hpml: {
|
hpml: {
|
||||||
|
type: Object as PropType<Hpml>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
text: this.hpml.interpolate(this.value.text),
|
text: this.hpml.interpolate(this.block.text),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'hpml.vars': {
|
'hpml.vars': {
|
||||||
handler() {
|
handler() {
|
||||||
this.text = this.hpml.interpolate(this.value.text);
|
this.text = this.hpml.interpolate(this.block.text);
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +1,72 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="iroscrza" :class="{ center: page.alignCenter, serif: page.font === 'serif' }" v-if="hpml">
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, onMounted, nextTick, onUnmounted, PropType } from 'vue';
|
||||||
import { parse } from '@syuilo/aiscript';
|
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 XBlock from './page.block.vue';
|
||||||
import { Hpml } from '@/scripts/hpml/evaluator';
|
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||||
import { url } from '@/config';
|
import { url } from '@/config';
|
||||||
|
import { $i } from '@/account';
|
||||||
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
XBlock
|
XBlock
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
page: {
|
page: {
|
||||||
type: Object,
|
type: Object as PropType<Record<string, any>>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setup(props, ctx) {
|
||||||
|
|
||||||
data() {
|
const hpml = new Hpml(props.page, {
|
||||||
return {
|
|
||||||
hpml: null,
|
|
||||||
faHeartS, faHeart
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.hpml = new Hpml(this.page, {
|
|
||||||
randomSeed: Math.random(),
|
randomSeed: Math.random(),
|
||||||
visitor: this.$i,
|
visitor: $i,
|
||||||
url: url,
|
url: url,
|
||||||
enableAiScript: !this.$store.state.disablePagesScript
|
enableAiScript: !defaultStore.state.disablePagesScript
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
onMounted(() => {
|
||||||
this.$nextTick(() => {
|
nextTick(() => {
|
||||||
if (this.page.script && this.hpml.aiscript) {
|
if (props.page.script && hpml.aiscript) {
|
||||||
let ast;
|
let ast;
|
||||||
try {
|
try {
|
||||||
ast = parse(this.page.script);
|
ast = parse(props.page.script);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
/*os.dialog({
|
/*os.dialog({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: 'Syntax error :('
|
text: 'Syntax error :('
|
||||||
});*/
|
});*/
|
||||||
return;
|
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();
|
onUnmounted(() => {
|
||||||
}).catch(e => {
|
if (hpml.aiscript) hpml.aiscript.abort();
|
||||||
console.error(e);
|
});
|
||||||
/*os.dialog({
|
|
||||||
type: 'error',
|
|
||||||
text: e
|
|
||||||
});*/
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.hpml.eval();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
beforeUnmount() {
|
return {
|
||||||
if (this.hpml.aiscript) this.hpml.aiscript.abort();
|
hpml,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -61,8 +61,10 @@ import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import XContainer from './page-editor.container.vue';
|
import XContainer from './page-editor.container.vue';
|
||||||
import MkTextarea from '@/components/ui/textarea.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 * as os from '@/os';
|
||||||
|
import { isLiteralValue } from '@/scripts/hpml/expr';
|
||||||
|
import { funcDefs } from '@/scripts/hpml/lib';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -166,7 +168,7 @@ export default defineComponent({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLiteralBlock(this.value)) return;
|
if (isLiteralValue(this.value)) return;
|
||||||
|
|
||||||
const empties = [];
|
const empties = [];
|
||||||
for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
|
for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
|
||||||
|
|
109
src/client/scripts/hpml/block.ts
Normal file
109
src/client/scripts/hpml/block.ts
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
import autobind from 'autobind-decorator';
|
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 { version } from '@/config';
|
||||||
import { AiScript, utils, values } from '@syuilo/aiscript';
|
import { AiScript, utils, values } from '@syuilo/aiscript';
|
||||||
import { createAiScriptEnv } from '../aiscript/api';
|
import { createAiScriptEnv } from '../aiscript/api';
|
||||||
import { collectPageVars } from '../collect-page-vars';
|
import { collectPageVars } from '../collect-page-vars';
|
||||||
import { initHpmlLib, initAiLib } from './lib';
|
import { initHpmlLib, initAiLib } from './lib';
|
||||||
import * as os from '@/os';
|
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
|
* Hpml evaluator
|
||||||
|
@ -94,7 +95,7 @@ export class Hpml {
|
||||||
public interpolate(str: string) {
|
public interpolate(str: string) {
|
||||||
if (str == null) return null;
|
if (str == null) return null;
|
||||||
return str.replace(/{(.+?)}/g, match => {
|
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();
|
return v == null ? 'NULL' : v.toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -158,72 +159,76 @@ export class Hpml {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
private evaluate(block: Block, scope: HpmlScope): any {
|
private evaluate(expr: Expr, scope: HpmlScope): any {
|
||||||
if (block.type === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.type === 'number') {
|
if (isLiteralValue(expr)) {
|
||||||
return parseInt(block.value, 10);
|
if (expr.type === null) {
|
||||||
}
|
|
||||||
|
|
||||||
if (block.type === 'text' || block.type === 'multiLineText') {
|
|
||||||
return this._interpolateScope(block.value || '', scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.type === 'textList') {
|
|
||||||
return this._interpolateScope(block.value || '', scope).trim().split('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.type === 'ref') {
|
|
||||||
return scope.getState(block.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.type === 'aiScriptVar') {
|
|
||||||
if (this.aiscript) {
|
|
||||||
try {
|
|
||||||
return utils.valToJs(this.aiscript.scope.get(block.value));
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Define user function
|
if (expr.type === 'number') {
|
||||||
if (isFnBlock(block)) {
|
return parseInt((expr.value as any), 10);
|
||||||
return {
|
}
|
||||||
slots: block.value.slots.map(x => x.name),
|
|
||||||
exec: (slotArg: Record<string, any>) => {
|
if (expr.type === 'text' || expr.type === 'multiLineText') {
|
||||||
return this.evaluate(block.value.expression, scope.createChildScope(slotArg, block.id));
|
return this._interpolateScope(expr.value || '', scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr.type === 'textList') {
|
||||||
|
return this._interpolateScope(expr.value || '', scope).trim().split('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr.type === 'ref') {
|
||||||
|
return scope.getState(expr.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr.type === 'aiScriptVar') {
|
||||||
|
if (this.aiscript) {
|
||||||
|
try {
|
||||||
|
return utils.valToJs(this.aiscript.scope.get(expr.value));
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} 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
|
// Call user function
|
||||||
if (block.type.startsWith('fn:')) {
|
if (expr.type.startsWith('fn:')) {
|
||||||
const fnName = block.type.split(':')[1];
|
const fnName = expr.type.split(':')[1];
|
||||||
const fn = scope.getState(fnName);
|
const fn = scope.getState(fnName);
|
||||||
const args = {} as Record<string, any>;
|
const args = {} as Record<string, any>;
|
||||||
for (let i = 0; i < fn.slots.length; i++) {
|
for (let i = 0; i < fn.slots.length; i++) {
|
||||||
const name = fn.slots[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);
|
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
|
// Call function
|
||||||
const fnName = block.type;
|
const fnName = expr.type;
|
||||||
const fn = (funcs as any)[fnName];
|
const fn = (funcs as any)[fnName];
|
||||||
if (fn == null) {
|
if (fn == null) {
|
||||||
throw new HpmlError(`No such function '${fnName}'`);
|
throw new HpmlError(`No such function '${fnName}'`);
|
||||||
} else {
|
} else {
|
||||||
return fn(...block.args.map(x => this.evaluate(x, scope)));
|
return fn(...expr.args.map(x => this.evaluate(x, scope)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
79
src/client/scripts/hpml/expr.ts
Normal file
79
src/client/scripts/hpml/expr.ts
Normal file
|
@ -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;
|
|
@ -3,52 +3,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
faMagic,
|
faMagic,
|
||||||
faSquareRootAlt,
|
faSquareRootAlt,
|
||||||
faAlignLeft,
|
faAlignLeft,
|
||||||
faShareAlt,
|
|
||||||
faPlus,
|
|
||||||
faMinus,
|
|
||||||
faTimes,
|
|
||||||
faDivide,
|
|
||||||
faList,
|
faList,
|
||||||
faQuoteRight,
|
faQuoteRight,
|
||||||
faEquals,
|
|
||||||
faGreaterThan,
|
|
||||||
faLessThan,
|
|
||||||
faGreaterThanEqual,
|
|
||||||
faLessThanEqual,
|
|
||||||
faNotEqual,
|
|
||||||
faDice,
|
|
||||||
faSortNumericUp,
|
faSortNumericUp,
|
||||||
faExchangeAlt,
|
|
||||||
faRecycle,
|
|
||||||
faIndent,
|
|
||||||
faCalculator,
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faFlag } from '@fortawesome/free-regular-svg-icons';
|
|
||||||
import { Hpml } from './evaluator';
|
import { Hpml } from './evaluator';
|
||||||
|
import { funcDefs } from './lib';
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Fn = {
|
export type Fn = {
|
||||||
slots: string[];
|
slots: string[];
|
||||||
|
@ -57,46 +21,6 @@ export type Fn = {
|
||||||
|
|
||||||
export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null;
|
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; }> = {
|
export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = {
|
||||||
text: { out: 'string', category: 'value', icon: faQuoteRight, },
|
text: { out: 'string', category: 'value', icon: faQuoteRight, },
|
||||||
multiLineText: { out: 'string', category: 'value', icon: faAlignLeft, },
|
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 type PageVar = { name: string; value: any; type: Type; };
|
||||||
|
|
||||||
export const envVarsDef: Record<string, Type> = {
|
export const envVarsDef: Record<string, Type> = {
|
||||||
|
@ -140,12 +60,6 @@ export const envVarsDef: Record<string, Type> = {
|
||||||
NULL: null,
|
NULL: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isLiteralBlock(v: Block) {
|
|
||||||
if (v.type === null) return true;
|
|
||||||
if (literalDefs[v.type]) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HpmlScope {
|
export class HpmlScope {
|
||||||
private layerdStates: Record<string, any>[];
|
private layerdStates: Record<string, any>[];
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
|
@ -2,9 +2,31 @@ import * as tinycolor from 'tinycolor2';
|
||||||
import Chart from 'chart.js';
|
import Chart from 'chart.js';
|
||||||
import { Hpml } from './evaluator';
|
import { Hpml } from './evaluator';
|
||||||
import { values, utils } from '@syuilo/aiscript';
|
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 * 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
|
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
|
||||||
Chart.pluginService.register({
|
Chart.pluginService.register({
|
||||||
beforeDraw: (chart, easing) => {
|
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 date = new Date();
|
||||||
const day = `${visitor ? visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
|
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'),
|
splitStrByLine: (a: string) => a.split('\n'),
|
||||||
pick: (list: any[], i: number) => list[i - 1],
|
pick: (list: any[], i: number) => list[i - 1],
|
||||||
listLen: (list: any[]) => list.length,
|
listLen: (list: any[]) => list.length,
|
||||||
random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * 100) < probability,
|
random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * 100) < probability,
|
||||||
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * (max - min + 1)),
|
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * (max - min + 1)),
|
||||||
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * list.length)],
|
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * list.length)],
|
||||||
dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
|
dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${expr.id}`)() * 100) < probability,
|
||||||
dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
|
dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${expr.id}`)() * (max - min + 1)),
|
||||||
dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
|
dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${expr.id}`)() * list.length)],
|
||||||
seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
|
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)),
|
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)],
|
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;
|
totalFactor += factor;
|
||||||
xs.push({ factor, text });
|
xs.push({ factor, text });
|
||||||
}
|
}
|
||||||
const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
|
const r = seedrandom(`${day}:${expr.id}`)() * totalFactor;
|
||||||
let stackedFactor = 0;
|
let stackedFactor = 0;
|
||||||
for (const x of xs) {
|
for (const x of xs) {
|
||||||
if (r >= stackedFactor && r <= stackedFactor + x.factor) {
|
if (r >= stackedFactor && r <= stackedFactor + x.factor) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import autobind from 'autobind-decorator';
|
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 = {
|
type TypeError = {
|
||||||
arg: number;
|
arg: number;
|
||||||
|
@ -20,10 +22,10 @@ export class HpmlTypeChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public typeCheck(v: Block): TypeError | null {
|
public typeCheck(v: Expr): TypeError | null {
|
||||||
if (isLiteralBlock(v)) return null;
|
if (isLiteralValue(v)) return null;
|
||||||
|
|
||||||
const def = funcDefs[v.type];
|
const def = funcDefs[v.type || ''];
|
||||||
if (def == null) {
|
if (def == null) {
|
||||||
throw new Error('Unknown type: ' + v.type);
|
throw new Error('Unknown type: ' + v.type);
|
||||||
}
|
}
|
||||||
|
@ -58,8 +60,8 @@ export class HpmlTypeChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public getExpectedType(v: Block, slot: number): Type {
|
public getExpectedType(v: Expr, slot: number): Type {
|
||||||
const def = funcDefs[v.type];
|
const def = funcDefs[v.type || ''];
|
||||||
if (def == null) {
|
if (def == null) {
|
||||||
throw new Error('Unknown type: ' + v.type);
|
throw new Error('Unknown type: ' + v.type);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +88,7 @@ export class HpmlTypeChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public infer(v: Block): Type {
|
public infer(v: Expr): Type {
|
||||||
if (v.type === null) return null;
|
if (v.type === null) return null;
|
||||||
if (v.type === 'text') return 'string';
|
if (v.type === 'text') return 'string';
|
||||||
if (v.type === 'multiLineText') return 'string';
|
if (v.type === 'multiLineText') return 'string';
|
||||||
|
@ -103,7 +105,7 @@ export class HpmlTypeChecker {
|
||||||
return pageVar.type;
|
return pageVar.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const envVar = envVarsDef[v.value];
|
const envVar = envVarsDef[v.value || ''];
|
||||||
if (envVar !== undefined) {
|
if (envVar !== undefined) {
|
||||||
return envVar;
|
return envVar;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue