mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 07:24:13 +00: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>
 | 
			
		||||
<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
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
		return {
 | 
			
		||||
			v: this.value.default,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		v() {
 | 
			
		||||
			this.hpml.updatePageVar(this.value.name, this.v);
 | 
			
		||||
			this.hpml.eval();
 | 
			
		||||
	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 {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
		return {
 | 
			
		||||
			v: this.value.default,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		v() {
 | 
			
		||||
			this.hpml.updatePageVar(this.value.name, this.v);
 | 
			
		||||
			this.hpml.eval();
 | 
			
		||||
	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 {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
		return {
 | 
			
		||||
			v: this.value.default,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		v() {
 | 
			
		||||
			this.hpml.updatePageVar(this.value.name, this.v);
 | 
			
		||||
			this.hpml.eval();
 | 
			
		||||
	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 {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
		return {
 | 
			
		||||
			v: this.value.default,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		v() {
 | 
			
		||||
			this.hpml.updatePageVar(this.value.name, this.v);
 | 
			
		||||
			this.hpml.eval();
 | 
			
		||||
	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 {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
		return {
 | 
			
		||||
			v: this.value.default,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		v() {
 | 
			
		||||
			this.hpml.updatePageVar(this.value.name, this.v);
 | 
			
		||||
			this.hpml.eval();
 | 
			
		||||
	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 {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 { 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 (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 {
 | 
			
		||||
		if (isLiteralValue(expr)) {
 | 
			
		||||
			if (expr.type === null) {
 | 
			
		||||
				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));
 | 
			
		||||
			if (expr.type === 'number') {
 | 
			
		||||
				return parseInt((expr.value as any), 10);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (expr.type === 'text' || expr.type === 'multiLineText') {
 | 
			
		||||
				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
 | 
			
		||||
		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)));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 {
 | 
			
		||||
	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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue