beyond-simulator-web 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  单用户自托管 Web 编辑器和磁盘存档预览,无需 Harness。复用共享 Studio、Lua Runtime、固定步长试玩 Worker、Pixi 渲染器及 `editor-ui` 编辑界面。
4
4
 
5
- ## 0.5.1 更新
5
+ ## 0.5.2 更新
6
6
 
7
- - 支持基础富文本,改进文本框高度不足时的整行显示。
8
- - 编辑画布与截图支持图片矩形遮罩和羽化预览。
9
- - 完善索引范围校验,保留旧存档编号、历史映射和脚本挂载,避免静默重编号。
7
+ - 支持界面/全屏动效素材搜索、编辑画布预览及 Lua 试玩播放,资源加载失败仅影响对应动效。
8
+ - 支持单个客户端控件和勾选组合的 GIA 导出,导出前可预览并检查依赖。
9
+ - 改进快速点击、拖拽和输入事件顺序;AI 启动试玩默认静音,可手动开启声音。
10
+
11
+ 完整公告见 [DSH 2.2.2 / Web 0.5.2 / MCP 0.5.2](../dsh-plugin/CHANGELOG.md#222--2026-10-30)。
10
12
 
11
13
  更新或修改字体后,重启 Web 服务并刷新页面。
12
14
 
@@ -78,6 +80,8 @@ Web/MCP 0.3.0、共享宿主所在 DSH 2.0.3:修复空工作区启动后跳过
78
80
 
79
81
  Lua `game.PlayAudio2D(id)` 触发白名单 MP3 按需缓存与播放,同源路由为 `/qxqy-audio/`,缓存目录可用 `QXQY_AUDIO_CACHE_DIR` 指定。首次自动播放被浏览器阻止时点击「开启声音」;支持停止、试玩暂停/继续和静音。参见 [音频说明](../studio/docs/audio-assets.md)。
80
82
 
83
+ 界面动效和全屏动效通过 `/qxqy-vfx/ct-v1/` 按需缓存 C/T WebM、图标和声音。`/editor` 支持静音动态预览,两个试玩入口都支持 Lua 驱动的动效播放;`/` 编辑模式仍为静态 PNG。资源失败不停止编辑/试玩。参见 [动效播放、缓存与截图边界](../studio/docs/ui-vfx-playback.md)。
84
+
81
85
  ## 部署与访问控制
82
86
 
83
87
  绑定非回环地址前,设置 `QXQY_WEB_PASSWORD`,登录用户名固定为 `simulator`。设置口令后,即使回环访问也要求认证。业务静态文件、API、SSE 与截图均受保护;写请求只接受 JSON 并校验浏览器 Origin。路径只能落在配置工作区内。
@@ -33,6 +33,7 @@ const elements = {
33
33
  stageShell: byId('stage-shell'),
34
34
  stageImage: byId('stage-image'),
35
35
  stageCanvas: byId('stage-canvas'),
36
+ stagePlay: byId('stage-play'),
36
37
  stageEmpty: byId('stage-empty'),
37
38
  stageLoading: byId('stage-loading'),
38
39
  activePath: byId('active-path'),
@@ -218,6 +219,8 @@ function renderStage() {
218
219
  const showPlay = hasArchive && mode === 'play' && Boolean(model?.play?.running)
219
220
  elements.stageImage.hidden = !showEditor
220
221
  elements.stageCanvas.hidden = !showPlay
222
+ elements.stagePlay.hidden = !showPlay
223
+ resizePlayStage()
221
224
  elements.stageEmpty.hidden = showEditor || showPlay
222
225
  elements.stageShell.classList.toggle('playing', mode === 'play' && Boolean(model?.play?.running))
223
226
  if (!hasArchive) {
@@ -243,6 +246,17 @@ function renderStage() {
243
246
  : t('编辑器静态预览 · 磁盘自动刷新')
244
247
  }
245
248
 
249
+ // The video planes and transparent Pixi canvas share this exact viewport, not
250
+ // the padded workspace around it. Keep letterboxing and clipping in one place.
251
+ function resizePlayStage() {
252
+ const width = model?.play?.width || 1600, height = model?.play?.height || 900
253
+ const scale = Math.max(0.01, Math.min((elements.stageShell.clientWidth - 70) / width, (elements.stageShell.clientHeight - 80) / height))
254
+ elements.stagePlay.style.width = `${width * scale}px`
255
+ elements.stagePlay.style.height = `${height * scale}px`
256
+ }
257
+ const stageResize = new ResizeObserver(resizePlayStage)
258
+ stageResize.observe(elements.stageShell)
259
+
246
260
  function refreshEditorImage() {
247
261
  if (!model?.activePath) return
248
262
  if (editorImageRefreshing) {
@@ -308,6 +322,7 @@ function syncPlayPolling() {
308
322
 
309
323
  async function onPlaySnapshot(next) {
310
324
  applyPlayStatus(next)
325
+ resizePlayStage()
311
326
  updatePlayChrome()
312
327
  }
313
328
 
@@ -391,8 +406,8 @@ const play = createPlaySession({
391
406
  onStatus: (next) => { applyPlayStatus(next); updatePlayChrome() },
392
407
  onError: (error) => notify(error.message, true),
393
408
  onAudioStatus: status => {
394
- elements.playAudio.textContent = !status.unlocked ? t('开启声音') : status.muted ? t('声音已关闭') : t('声音已开启')
395
- elements.playAudio.title = status.error || (!status.unlocked ? t('点击或在画布内操作以解锁音效') : t('切换静音'))
409
+ elements.playAudio.textContent = status.muted ? t('声音已关闭') : !status.unlocked ? t('开启声音') : t('声音已开启')
410
+ elements.playAudio.title = status.error || (status.muted || status.unlocked ? t('切换静音') : t('点击或在画布内操作以解锁音效'))
396
411
  },
397
412
  isActive: () => mode === 'play',
398
413
  })
@@ -499,5 +514,5 @@ events.addEventListener('state', (event) => {
499
514
  })
500
515
 
501
516
  setInterval(() => { void refreshState({ quiet: true }) }, 5000)
502
- addEventListener('pagehide', () => play.destroy())
517
+ addEventListener('pagehide', () => { stageResize.disconnect(); play.destroy() })
503
518
  void refreshState()