Skip to content

Commit 9571b68

Browse files
authored
Merge pull request #1078 from goplus/dev
Release v1.5.3
2 parents cc14a1d + da09750 commit 9571b68

File tree

13 files changed

+161
-48
lines changed

13 files changed

+161
-48
lines changed

spx-gui/src/components/community/user/UserItem.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const userRoute = computed(() => getUserPageRoute(props.user.username))
2020
<UserAvatar class="avatar" :user="user.username" />
2121
<RouterUILink class="name" type="boring" :to="userRoute">{{ user.displayName }}</RouterUILink>
2222
<UserJoinedAt class="joined-at" :time="user.createdAt" />
23-
<TextView class="description" :text="user.description" />
23+
<TextView v-if="!!user.description" class="description" :text="user.description" />
2424
<FollowButton class="follow" :name="user.username" />
2525
</li>
2626
</template>

spx-gui/src/components/editor/code-editor/code-text-editor/tools/index.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export const gameCategory: ToolCategory = {
188188
},
189189
{
190190
label: { en: 'Others', zh: '其他' },
191-
tools: [spx.rand, gop.println]
191+
tools: [spx.rand, gop.println, spx.getWidget]
192192
}
193193
]
194194
}
@@ -253,6 +253,23 @@ export function getVariableCategory(project: Project): ToolCategory {
253253
})
254254
})
255255

256+
groups.push({
257+
label: { en: 'Widgets', zh: '控件' },
258+
tools: project.stage.widgets.map((widget) => {
259+
const keyword = `"${widget.name}"`
260+
return {
261+
type: ToolType.variable,
262+
target: ToolContext.all,
263+
keyword,
264+
desc: { en: `Widget "${widget.name}"`, zh: `控件 ${widget.name}` },
265+
usage: {
266+
sample: keyword,
267+
insertText: keyword
268+
}
269+
}
270+
})
271+
})
272+
256273
if (project.selectedSprite != null) {
257274
groups.push({
258275
label: {

spx-gui/src/components/editor/code-editor/code-text-editor/tools/spx.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,20 @@ export const exit: Tool = {
974974
}
975975
}
976976

977+
export const getWidget: Tool = {
978+
type: ToolType.function,
979+
callEffect: ToolCallEffect.read,
980+
target: ToolContext.all,
981+
keyword: 'getWidget',
982+
desc: { en: 'Get the widget by given name', zh: '通过指定名称获取控件' },
983+
usage: {
984+
sample: 'getWidget(Monitor, "monitor1")',
985+
insertText: 'getWidget(${1:Monitor}, ${2:name})'
986+
}
987+
}
988+
989+
// TODO: definition for widget methods
990+
977991
function defineConst(name: string, desc: LocaleMessage): Tool {
978992
name = name[0].toUpperCase() + name.slice(1) // it's strange, but required
979993
return {

spx-gui/src/components/editor/preview/EditorPreview.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,19 @@
1919
</template>
2020

2121
<script lang="ts" setup>
22-
import { ref } from 'vue'
22+
import { onMounted, ref } from 'vue'
2323
import { useEditorCtx } from '@/components/editor/EditorContextProvider.vue'
2424
import { UICard, UICardHeader, UIButton, UIFullScreenModal } from '@/components/ui'
2525
import StageViewer from './stage-viewer/StageViewer.vue'
26-
import RunnerContainer from '@/components/project/runner/RunnerContainer.vue'
26+
import RunnerContainer, { preload as preloadRunner } from '@/components/project/runner/RunnerContainer.vue'
2727
2828
let show = ref(false)
2929
3030
const editorCtx = useEditorCtx()
31+
32+
onMounted(() => {
33+
preloadRunner()
34+
})
3135
</script>
3236

3337
<style scoped lang="scss">

spx-gui/src/components/editor/sprite/animation/AnimationSettings.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<template>
22
<section class="wrapper">
3-
<UIDropdown trigger="manual" :visible="activeSetting != null" placement="top" @click-outside="handleClickOutside">
3+
<UIDropdown
4+
trigger="manual"
5+
:visible="activeSetting != null"
6+
placement="top"
7+
@click-outside="handleClickOutside"
8+
@update:visible="handleDropdownVisibleUpdate"
9+
>
410
<template #trigger>
511
<ul class="settings">
612
<li class="setting" :class="{ active: activeSetting === 'duration' }" @click="handleSummaryClick('duration')">
@@ -75,6 +81,10 @@ function handleClickOutside(e: MouseEvent) {
7581
if (isInPopup(e.target as HTMLElement | null)) return
7682
activeSetting.value = null
7783
}
84+
85+
function handleDropdownVisibleUpdate(visible: boolean) {
86+
if (!visible) activeSetting.value = null
87+
}
7888
</script>
7989

8090
<style lang="scss" scoped>

spx-gui/src/components/project/runner/IframeDisplay.vue

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,34 @@
11
<template>
22
<iframe ref="iframe" class="iframe" frameborder="0" src="about:blank" />
33
</template>
4+
5+
<script lang="ts">
6+
import { ref, watch } from 'vue'
7+
import rawRunnerHtml from '@/assets/ispx/runner.html?raw'
8+
import wasmExecUrl from '@/assets/wasm_exec.js?url'
9+
import wasmUrl from '@/assets/ispx/main.wasm?url'
10+
11+
function addPrefetchLink(url: string) {
12+
// Use `prefetch` instead of `preload`:
13+
// * `preload` indicates higher priority than `prefetch`. Preloaded content are expected to be used soon. For example, chrome will warn if the preloaded content is not used within 3 or 5 seconds. While project here will not be runned until the user clicks some "run" button.
14+
// * `preload` results are not shared across different documents, while the iframe content is a different document. The "preloading" is meaningful only when the HTTP cache is shared, which is more like the case of `prefetch`.
15+
const link = document.createElement('link')
16+
link.rel = 'prefetch'
17+
link.href = url
18+
link.crossOrigin = 'anonymous'
19+
link.onload = link.onerror = () => {
20+
document.head.removeChild(link)
21+
}
22+
document.head.appendChild(link)
23+
}
24+
25+
// preload resources (for example, wasm files) to accelerate the loading
26+
export function preload() {
27+
addPrefetchLink(wasmExecUrl)
28+
addPrefetchLink(wasmUrl)
29+
}
30+
</script>
31+
432
<script setup lang="ts">
533
const emit = defineEmits<{
634
console: [type: 'log' | 'warn', args: unknown[]]
@@ -12,12 +40,6 @@ interface IframeWindow extends Window {
1240
console: typeof console
1341
}
1442
15-
import { ref } from 'vue'
16-
import rawRunnerHtml from '@/assets/ispx/runner.html?raw'
17-
import wasmExecUrl from '@/assets/wasm_exec.js?url'
18-
import wasmUrl from '@/assets/ispx/main.wasm?url'
19-
import { watch } from 'vue'
20-
2143
const props = defineProps<{ zipData: ArrayBuffer | Uint8Array }>()
2244
2345
const iframe = ref<HTMLIFrameElement>()

spx-gui/src/components/project/runner/ProjectRunner.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
</div>
77
</template>
88

9+
<script lang="ts">
10+
import IframeDisplay, { preload } from './IframeDisplay.vue'
11+
export { preload }
12+
</script>
13+
914
<script lang="ts" setup>
1015
import { onUnmounted, ref } from 'vue'
1116
import { registerPlayer } from '@/utils/player-registry'
1217
import { useFileUrl } from '@/utils/file'
1318
import { Project } from '@/models/project'
1419
import { UIImg, UILoading } from '@/components/ui'
15-
import IframeDisplay from './IframeDisplay.vue'
1620
1721
const props = defineProps<{ project: Project }>()
1822

spx-gui/src/components/project/runner/RunnerContainer.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,16 @@
4848
</div>
4949
</div>
5050
</template>
51+
52+
<script lang="ts">
53+
import ProjectRunner, { preload } from './ProjectRunner.vue'
54+
export { preload }
55+
</script>
56+
5157
<script setup lang="ts">
5258
import { onMounted, ref, type CSSProperties, watch, nextTick } from 'vue'
5359
import dayjs from 'dayjs'
5460
import type { Project } from '@/models/project'
55-
import ProjectRunner from './ProjectRunner.vue'
5661
import { usePublishProject } from '@/components/project'
5762
import { UIButton, UIIcon, UIModalClose } from '@/components/ui'
5863
import { useMessageHandle } from '@/utils/exception'

spx-gui/src/components/ui/UIDropdown.vue

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function useDropdown() {
3232
</script>
3333

3434
<script setup lang="ts">
35-
import { inject, provide, ref, type InjectionKey, computed, type CSSProperties } from 'vue'
35+
import { inject, provide, ref, type InjectionKey, computed, type CSSProperties, watchEffect } from 'vue'
3636
import { NPopover } from 'naive-ui'
3737
import { usePopupContainer } from './utils'
3838
@@ -89,21 +89,33 @@ function handleUpdateShow(show: boolean) {
8989
emit('update:visible', show)
9090
}
9191
92+
function setVisible(visible: boolean) {
93+
// `NPopover.setShow` sets show status in uncontrolled mode without triggering the `on-update:show` callback.
94+
// So we need to manually trigger the `on-update:show` callback.
95+
nPopoverRef.value?.setShow(visible)
96+
handleUpdateShow(visible)
97+
}
98+
9299
function handleClickOutside(e: MouseEvent) {
93100
const triggerEl = nPopoverRef.value?.binderInstRef?.targetRef
94101
// naive-ui triggers `clickoutside` event when trigger-element clicked, so we need to fix it
95102
if (triggerEl != null && triggerEl.contains(e.target as Node)) return
96103
emit('clickOutside', e)
97104
}
98105
99-
provide(dropdownCtrlKey, {
100-
setVisible(visible) {
101-
// `NPopover.setShow` sets show status in uncontrolled mode without triggering the `on-update:show` callback.
102-
// So we need to manually trigger the `on-update:show` callback.
103-
nPopoverRef.value?.setShow(visible)
104-
handleUpdateShow(visible)
106+
watchEffect((onCleanup) => {
107+
// Currently, pressing the `Escape` key closes all modals and dropdowns.
108+
// TODO: Ideally, only the topmost modal or dropdown should be closed.
109+
function handleDocumentKeydown(e: KeyboardEvent) {
110+
if (e.key === 'Escape') setVisible(false)
105111
}
112+
window.addEventListener('keydown', handleDocumentKeydown)
113+
onCleanup(() => {
114+
window.removeEventListener('keydown', handleDocumentKeydown)
115+
})
106116
})
117+
118+
provide(dropdownCtrlKey, { setVisible })
107119
</script>
108120

109121
<style lang="scss" scoped></style>

spx-gui/src/components/ui/modal/UIDropdownModal.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<section class="wrapper">
2+
<form class="wrapper" @submit.prevent="emit('confirm')">
33
<header class="header">
44
<h4 class="title">{{ title }}</h4>
55
<UIModalClose class="close" @click="emit('cancel')" />
@@ -12,11 +12,11 @@
1212
<UIButton type="boring" @click="emit('cancel')">
1313
{{ $t({ en: 'Cancel', zh: '取消' }) }}
1414
</UIButton>
15-
<UIButton type="primary" @click="emit('confirm')">
15+
<UIButton type="primary" html-type="submit">
1616
{{ $t({ en: 'Confirm', zh: '确认' }) }}
1717
</UIButton>
1818
</footer>
19-
</section>
19+
</form>
2020
</template>
2121
<script setup lang="ts">
2222
import { UIButton, UIDivider } from '@/components/ui'

0 commit comments

Comments
 (0)