加入追踪

This commit is contained in:
tianmo committed 2026-10-28 14:32:52 +08:00
1 parent 859c78173a
commit ca719ba1d4
17 files changed
+77 -47

No files matched your search

+4
View File
@@ -36,6 +36,7 @@
:disabled="!canUseSection(section.key)"
:aria-selected="activeSection === section.key && canUseSection(section.key)"
:title="canUseSection(section.key) ? $t(section.titleKey) : $t('settings.floatingMenuParticleOnly')"
:data-matomo-event="`navigation.${section.key}`"
@click="activateSection(section.key)"
>
<span class="floating-section-index">{{ index + 1 }}</span>
@@ -48,6 +49,7 @@
:class="{ collapsed: !bottomPanelExpanded }"
:aria-expanded="bottomPanelExpanded"
:title="timelineCollapsed ? '展开时间轴' : '点击收起,拖动调整高度'"
data-matomo-event="timeline.toggle"
@pointerdown="onTimelineTogglePointerDown"
@click="toggleTimeline"
>
@@ -78,6 +80,7 @@ import TransferProgressOverlay from './views/TransferProgressOverlay.vue'
import { transferProgress } from './editor/transferProgress'
import { provideParticlePanel } from './views/particle-panel/particlePanelContext'
import { PARTICLE_SECTIONS } from './views/particle-panel/particleSections'
import { trackMatomoEvent } from './editor/analytics'
const store = useParticleStore()
installSystemSettingsCache(store)
@@ -188,6 +191,7 @@ function onTimelineTogglePointerDown(e: PointerEvent) {
window.removeEventListener('pointerup', finish)
window.removeEventListener('pointercancel', finish)
if (!dragging) return
trackMatomoEvent('timeline.resize')
if (timelineH.value > 0) saveTimelineHeight()
// 拖拽后的 click 与本次手势属于同一操作,不能再次切换折叠。
ignoreTimelineToggleClick = true
+23
View File
@@ -0,0 +1,23 @@
type MatomoWindow = Window & { _paq?: unknown[][] }
export function trackMatomoEvent(eventName: string) {
if (!import.meta.env.PROD) return
const separator = eventName.indexOf('.')
if (separator <= 0 || separator === eventName.length - 1) return
const target = window as MatomoWindow
;(target._paq ||= []).push([
'trackEvent',
eventName.slice(0, separator),
eventName.slice(separator + 1),
])
}
export function installMatomoEventTracking() {
if (!import.meta.env.PROD) return
document.addEventListener('click', (event) => {
const target = event.target
if (!(target instanceof Element)) return
const tracked = target.closest<HTMLElement>('[data-matomo-event]')
if (tracked?.dataset.matomoEvent) trackMatomoEvent(tracked.dataset.matomoEvent)
}, true)
}
+2
View File
@@ -3,5 +3,7 @@ import { createPinia } from 'pinia'
import App from './App.vue'
import { i18n } from './i18n'
import './style.css'
import { installMatomoEventTracking } from './editor/analytics'
installMatomoEventTracking()
createApp(App).use(createPinia()).use(i18n).mount('#app')
+5 -4
View File
@@ -1,7 +1,7 @@
<template>
<div class="image-card">
<div class="imgrow">
<button class="img-thumb" :title="supportsSequence && resource.imageMode === 'sequence' ? $t('image.chooseSequence') : $t('image.chooseLocal')" @click="pickImage">
<button class="img-thumb" data-matomo-event="resources.pick-image" :title="supportsSequence && resource.imageMode === 'sequence' ? $t('image.chooseSequence') : $t('image.chooseLocal')" @click="pickImage">
<img v-if="displayPreviewUrl" :src="displayPreviewUrl" :alt="resource.textureName" />
<svg v-else viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#8a93bb" stroke-width="1.6"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.6"/><path d="M21 15l-5-5-9 9"/></svg>
</button>
@@ -10,8 +10,8 @@
<label class="row"><span>{{ $t('image.name') }}</span><input v-model="resource.textureName" type="text" class="inp" :placeholder="namePlaceholder" /></label>
</div>
<div class="img-actions">
<button class="mini-btn2" :title="$t('image.restore', { name: defaultFile })" @click="$emit('reset', resource.id)">⟳</button>
<button class="mini-btn2" :title="$t('image.deleteResource')" :disabled="resourceCount === 1" @click="$emit('remove', resource.id)">🗑</button>
<button class="mini-btn2" data-matomo-event="resources.reset-image" :title="$t('image.restore', { name: defaultFile })" @click="$emit('reset', resource.id)">⟳</button>
<button class="mini-btn2" data-matomo-event="resources.delete-image" :title="$t('image.deleteResource')" :disabled="resourceCount === 1" @click="$emit('remove', resource.id)">🗑</button>
</div>
</div>
<div class="thumb-note">{{ displayFileName }}</div>
@@ -31,7 +31,7 @@
</select>
</label>
<label class="row"><span>{{ $t('image.playbackFps') }}</span><input v-model.number="resource.sequenceFps" type="number" min="1" max="120" step="1" class="inp" /></label>
<button class="sequence-pick" @click="$emit('pickSequence', resource.id)">{{ $t('image.chooseSequenceMulti') }}</button>
<button class="sequence-pick" data-matomo-event="resources.pick-sequence" @click="$emit('pickSequence', resource.id)">{{ $t('image.chooseSequenceMulti') }}</button>
<div class="sequence-note">{{ sequenceOrderText }}</div>
</template>
<div class="resource-weight-row">
@@ -46,6 +46,7 @@
@update:model-value="$emit('updateWeight', resource.id, $event)"
/>
<button
data-matomo-event="resources.toggle-lock"
class="weight-lock"
:class="{ on: resource.locked }"
:disabled="resourceCount === 1"
+7 -7
View File
@@ -3,16 +3,16 @@
<!-- 控制按钮 -->
<Teleport defer to="#timeline-controls-host">
<div class="tl-controls">
<button class="tl-btn" :class="{ on: store.timeline.playing }" @click="store.togglePlay()" :title="store.timeline.playing ? $t('timeline.pause') : $t('timeline.play')">
<button class="tl-btn" data-matomo-event="timeline.play-toggle" :class="{ on: store.timeline.playing }" @click="store.togglePlay()" :title="store.timeline.playing ? $t('timeline.pause') : $t('timeline.play')">
{{ store.timeline.playing ? '❚❚' : '▶' }}
</button>
<button class="tl-btn" @click="store.toStart()" :title="$t('timeline.toStart')">⏮</button>
<button class="tl-btn" @click="store.previousFrame()" :title="$t('timeline.previous')">◀</button>
<button class="tl-btn" @click="store.nextFrame()" :title="$t('timeline.next')">▶</button>
<button class="tl-btn" @click="store.toEnd()" :title="$t('timeline.toEnd')">⏭</button>
<button class="tl-btn" :class="{ on: store.timeline.loop }" :title="$t('timeline.continuousLoop')" :aria-label="$t('timeline.continuousLoop')" @click="store.toggleLoop()">⟳</button>
<button class="tl-btn" data-matomo-event="timeline.to-start" @click="store.toStart()" :title="$t('timeline.toStart')">⏮</button>
<button class="tl-btn" data-matomo-event="timeline.previous-frame" @click="store.previousFrame()" :title="$t('timeline.previous')">◀</button>
<button class="tl-btn" data-matomo-event="timeline.next-frame" @click="store.nextFrame()" :title="$t('timeline.next')">▶</button>
<button class="tl-btn" data-matomo-event="timeline.to-end" @click="store.toEnd()" :title="$t('timeline.toEnd')">⏭</button>
<button class="tl-btn" data-matomo-event="timeline.loop-toggle" :class="{ on: store.timeline.loop }" :title="$t('timeline.continuousLoop')" :aria-label="$t('timeline.continuousLoop')" @click="store.toggleLoop()">⟳</button>
<div class="tl-ab-loop">
<button class="tl-ab-toggle" :class="{ on: store.timeline.abLoop }" :title="$t('timeline.abLoop')" @click="store.toggleABLoop()">
<button class="tl-ab-toggle" data-matomo-event="timeline.ab-toggle" :class="{ on: store.timeline.abLoop }" :title="$t('timeline.abLoop')" @click="store.toggleABLoop()">
<span>{{ $t('timeline.abLoopShort') }}</span><i class="tl-switch"><b></b></i>
</button>
<div v-if="store.timeline.abLoop" class="tl-ab-popover" @pointerdown.stop>
+2 -2
View File
@@ -3,7 +3,7 @@
<div class="group" v-show="sectionVisible('emitMode')">
<div class="group-head" @click="toggle('emitMode')">
<span class="caret">{{ collapsed.emitMode ? '▸' : '▾' }}</span><span>{{ $t('emission.title') }}</span>
<button class="group-reset" :title="$t('emission.reset')" @click.stop="resetModule('emitMode')">{{ $t('common.reset') }}</button>
<button class="group-reset" data-matomo-event="emission.reset" :title="$t('emission.reset')" @click.stop="resetModule('emitMode')">{{ $t('common.reset') }}</button>
</div>
<div class="group-body" v-show="sectionOpen('emitMode')">
<div class="segs">
@@ -65,7 +65,7 @@
<div class="group" v-show="sectionVisible('shape')">
<div class="group-head" @click="toggle('shape')">
<span class="caret">{{ collapsed.shape ? '▸' : '▾' }}</span><span>{{ $t('emission.shapeTitle') }}</span>
<button class="group-reset" :title="$t('emission.shapeReset')" @click.stop="resetModule('shape')">{{ $t('common.reset') }}</button>
<button class="group-reset" data-matomo-event="shape.reset" :title="$t('emission.shapeReset')" @click.stop="resetModule('shape')">{{ $t('common.reset') }}</button>
</div>
<div class="group-body" v-show="sectionOpen('shape')">
<label class="row"><span>{{ $t('emission.shape') }}</span>
@@ -3,7 +3,7 @@
<div class="group">
<div class="group-head" @click="toggle('export')">
<span class="caret">{{ collapsed.export ? '▸' : '▾' }}</span><span>{{ $t('export.title') }}</span>
<button class="group-reset" :title="$t('export.reset')" @click.stop="resetModule('export')">{{ $t('common.reset') }}</button>
<button class="group-reset" data-matomo-event="export.reset" :title="$t('export.reset')" @click.stop="resetModule('export')">{{ $t('common.reset') }}</button>
</div>
<div class="group-body export-settings" v-show="sectionOpen('export')">
<label class="export-field">
@@ -3,7 +3,7 @@
<div class="group">
<div class="group-head" @click="toggle('look')">
<span class="caret">{{ collapsed.look ? '▸' : '▾' }}</span><span>{{ $t('appearance.title') }}</span>
<button class="group-reset" :title="$t('appearance.reset')" @click.stop="resetModule('look')">{{ $t('common.reset') }}</button>
<button class="group-reset" data-matomo-event="appearance.reset" :title="$t('appearance.reset')" @click.stop="resetModule('look')">{{ $t('common.reset') }}</button>
</div>
<div class="group-body" v-show="sectionOpen('look')">
<!-- 透明度 -->
@@ -83,7 +83,7 @@
<!-- 图片资源 -->
<div class="look-title image-title">
<span>{{ $t('appearance.imageResources') }}</span>
<button class="image-add" :title="$t('appearance.addImage')" @click="addImageResource">+</button>
<button class="image-add" data-matomo-event="resources.add-image" :title="$t('appearance.addImage')" @click="addImageResource">+</button>
</div>
<ImageResourceCard
v-for="resource in sys.config.imageResources"
@@ -3,7 +3,7 @@
<div class="group">
<div class="group-head" @click="toggle('attr')">
<span class="caret">{{ collapsed.attr ? '▸' : '▾' }}</span><span>{{ $t('attributes.title') }}</span>
<button class="group-reset" :title="$t('attributes.reset')" @click.stop="resetModule('attr')">{{ $t('common.reset') }}</button>
<button class="group-reset" data-matomo-event="attributes.reset" :title="$t('attributes.reset')" @click.stop="resetModule('attr')">{{ $t('common.reset') }}</button>
</div>
<div class="group-body attr-body" v-show="sectionOpen('attr')">
<div class="attr-section-title">{{ $t('attributes.initialParams') }}</div>
@@ -1,12 +1,12 @@
<template>
<div class="spine-export-actions">
<button type="button" class="export-spine-json" :disabled="exportBusy" :title="exportBusy ? $t('export.baking') : $t('export.exportJsonTitle', { version: exportSettings.spineVersion })" @click="exportSpineJson">
<button type="button" class="export-spine-json" data-matomo-event="export.json" :disabled="exportBusy" :title="exportBusy ? $t('export.baking') : $t('export.exportJsonTitle', { version: exportSettings.spineVersion })" @click="exportSpineJson">
{{ exportBusy && exportTarget === 'json' ? $t('export.preparing') : $t('export.exportJson') }}
</button>
<button type="button" class="export-spine-images" :disabled="exportBusy" :title="$t('export.exportImagesTitle')" @click="exportSpineImages">
<button type="button" class="export-spine-images" data-matomo-event="export.images" :disabled="exportBusy" :title="$t('export.exportImagesTitle')" @click="exportSpineImages">
{{ exportBusy && exportTarget === 'images' ? $t('export.organizingImages') : $t('export.exportImages') }}
</button>
<button type="button" class="export-spine-project" :disabled="exportBusy || !connectorReady" :title="connectorButtonTitle" @click="exportSpineProject">
<button type="button" class="export-spine-project" data-matomo-event="export.spine-project" :disabled="exportBusy || !connectorReady" :title="connectorButtonTitle" @click="exportSpineProject">
<span>{{ exportBusy && exportTarget === 'spine' ? $t('export.generatingProject') : $t('export.exportSpine') }}</span>
<small v-if="!exportBusy && connectorDisabledReason">{{ connectorDisabledReason }}</small>
</button>
@@ -3,7 +3,7 @@
<div class="group">
<div class="group-head" @click="toggle('mods')">
<span class="caret">{{ collapsed.mods ? '▸' : '▾' }}</span><span>{{ $t('modifiers.title') }}</span>
<button class="group-reset" :title="$t('modifiers.reset')" @click.stop="resetModule('mods')">{{ $t('common.reset') }}</button>
<button class="group-reset" data-matomo-event="modifiers.reset" :title="$t('modifiers.reset')" @click.stop="resetModule('mods')">{{ $t('common.reset') }}</button>
</div>
<div class="group-body" v-show="sectionOpen('mods')">
<div class="modifier-item damping-item">
@@ -142,7 +142,7 @@
<div v-if="sys.config.trail" class="modifier-params trail-params">
<div class="trail-section-head">
<div class="trail-section-title">{{ $t('modifiers.trailImages') }}</div>
<button class="image-add" :title="$t('modifiers.addTrailImage')" @click="addTrailResource">+</button>
<button class="image-add" data-matomo-event="resources.add-trail-image" :title="$t('modifiers.addTrailImage')" @click="addTrailResource">+</button>
</div>
<ImageResourceCard
v-for="resource in sys.config.trailResources"
@@ -2,12 +2,12 @@
<div class="group config-management">
<div class="group-head" @click="toggle('config')">
<span class="caret">{{ collapsed.config ? '▸' : '▾' }}</span><span>{{ $t('config.title') }}</span>
<button type="button" class="group-download" @click.stop="downloadCurrentProject">{{ $t('config.local.downloadCurrent') }}</button>
<button type="button" class="group-download" data-matomo-event="config.download-current" @click.stop="downloadCurrentProject">{{ $t('config.local.downloadCurrent') }}</button>
</div>
<div class="group-body config-management-body" v-show="sectionOpen('config')">
<div class="config-toolbar">
<button :title="$t('config.saveTitle')" @click="saveConfig">{{ $t('config.save') }}</button>
<button :title="$t('config.loadTitle')" @click="configFileInput?.click()">{{ $t('config.load') }}</button>
<button :title="$t('config.saveTitle')" data-matomo-event="config.save" @click="saveConfig">{{ $t('config.save') }}</button>
<button :title="$t('config.loadTitle')" data-matomo-event="config.load-file" @click="configFileInput?.click()">{{ $t('config.load') }}</button>
<select v-model="selectedPreset" :aria-label="$t('config.presets')" :disabled="!presetOptions.length || presetLoading" @pointerdown="refreshPresets" @focus="refreshPresets" @change="loadSelectedPreset">
<option value="">{{ presetOptions.length ? $t('config.selectPreset') : $t('config.noPresets') }}</option>
<option v-for="preset in presetOptions" :key="preset.file" :value="preset.file">{{ presetDisplayName(preset) }}</option>
@@ -35,18 +35,18 @@
<span class="project-name">{{ project.name }}</span>
<span v-if="store.settings.showProjectSize" class="project-size">{{ formatProjectSize(project.size) }}</span>
<div class="project-actions" @dblclick.stop>
<button type="button" @click.stop="removeLocalProject(project)">{{ $t('config.local.delete') }}</button>
<button type="button" @click.stop="downloadLocalProject(project)">{{ $t('config.local.download') }}</button>
<button type="button" data-matomo-event="config.delete-project" @click.stop="removeLocalProject(project)">{{ $t('config.local.delete') }}</button>
<button type="button" data-matomo-event="config.download-project" @click.stop="downloadLocalProject(project)">{{ $t('config.local.download') }}</button>
</div>
</div>
</div>
<div v-else class="project-empty">{{ $t('config.local.empty') }}</div>
</div>
<div class="project-bulk-actions">
<button type="button" :disabled="projectStorageBusy" @click="downloadAllLocalProjects">
<button type="button" :disabled="projectStorageBusy" data-matomo-event="config.download-all" @click="downloadAllLocalProjects">
{{ $t('config.local.downloadAll') }}
</button>
<button type="button" :disabled="projectStorageBusy" @click="projectFilesInput?.click()">
<button type="button" :disabled="projectStorageBusy" data-matomo-event="config.load-all" @click="projectFilesInput?.click()">
{{ $t('config.local.loadAll') }}
</button>
<input ref="projectFilesInput" hidden type="file" multiple accept=".json,application/json" @change="loadAllProjectFiles" />
@@ -4,11 +4,11 @@
<div class="group-head scene-head" @click="toggle('scene')">
<span class="caret">{{ collapsed.scene ? '▸' : '▾' }}</span><span>{{ $t('scene.title') }}</span>
<div class="scene-actions">
<button class="scene-add" :title="$t('scene.addParticle')" @click.stop="addSys">{{ $t('scene.addParticleShort') }}</button>
<button class="scene-add" :title="$t('scene.addCollider')" @click.stop="addCollider">{{ $t('scene.addColliderShort') }}</button>
<button class="scene-add" :title="$t('scene.addPath')" @click.stop="addPath">{{ $t('scene.addPathShort') }}</button>
<button class="scene-add" :title="$t('scene.addSpine')" @click.stop="addSpine">+Spine</button>
<button class="scene-add" :disabled="!canCopyObject" :title="$t('scene.copyTitle')" @click.stop="copySelectedObject">+Copy</button>
<button class="scene-add" data-matomo-event="scene.add-particle" :title="$t('scene.addParticle')" @click.stop="addSys">{{ $t('scene.addParticleShort') }}</button>
<button class="scene-add" data-matomo-event="scene.add-collider" :title="$t('scene.addCollider')" @click.stop="addCollider">{{ $t('scene.addColliderShort') }}</button>
<button class="scene-add" data-matomo-event="scene.add-path" :title="$t('scene.addPath')" @click.stop="addPath">{{ $t('scene.addPathShort') }}</button>
<button class="scene-add" data-matomo-event="scene.add-spine" :title="$t('scene.addSpine')" @click.stop="addSpine">+Spine</button>
<button class="scene-add" data-matomo-event="scene.copy" :disabled="!canCopyObject" :title="$t('scene.copyTitle')" @click.stop="copySelectedObject">+Copy</button>
</div>
</div>
<div class="group-body" v-show="sectionOpen('scene')">
@@ -24,11 +24,11 @@
<span v-if="entry.depth > 0" class="sub-branch">└</span>
<span v-else class="tdot">●</span>
<span class="tlabel">{{ entry.system.config.name || 'ParticleSystem' }}</span>
<button class="scene-object-eye" :class="{ off: entry.system.visible === false }" :title="entry.system.visible === false ? $t('scene.showParticle') : $t('scene.hideParticle')" :aria-label="entry.system.visible === false ? $t('scene.showParticle') : $t('scene.hideParticle')" @click.stop="entry.system.visible = entry.system.visible === false">
<button class="scene-object-eye" data-matomo-event="scene.toggle-particle" :class="{ off: entry.system.visible === false }" :title="entry.system.visible === false ? $t('scene.showParticle') : $t('scene.hideParticle')" :aria-label="entry.system.visible === false ? $t('scene.showParticle') : $t('scene.hideParticle')" @click.stop="entry.system.visible = entry.system.visible === false">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="entry.system.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
</button>
<span class="scene-object-kind" :class="{ 'sub-emitter-kind': entry.depth > 0 }">{{ entry.depth > 0 ? $t('scene.subEmitter') : $t('scene.particle') }}</span>
<button class="scene-object-delete" :title="$t('scene.deleteParticle')" @click.stop="store.removeSystem(entry.system.id)">✕</button>
<button class="scene-object-delete" data-matomo-event="scene.delete-particle" :title="$t('scene.deleteParticle')" @click.stop="store.removeSystem(entry.system.id)">✕</button>
</div>
<div
v-for="collider in store.colliders"
@@ -39,11 +39,11 @@
>
<span class="tdot collision-dot">■</span>
<span class="tlabel">{{ collider.name }}</span>
<button class="scene-object-eye" :class="{ off: collider.visible === false }" :title="collider.visible === false ? $t('scene.showCollider') : $t('scene.hideCollider')" :aria-label="collider.visible === false ? $t('scene.showCollider') : $t('scene.hideCollider')" @click.stop="collider.visible = collider.visible === false">
<button class="scene-object-eye" data-matomo-event="scene.toggle-collider" :class="{ off: collider.visible === false }" :title="collider.visible === false ? $t('scene.showCollider') : $t('scene.hideCollider')" :aria-label="collider.visible === false ? $t('scene.showCollider') : $t('scene.hideCollider')" @click.stop="collider.visible = collider.visible === false">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="collider.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
</button>
<span class="scene-object-kind">{{ $t('scene.collision') }}</span>
<button class="scene-object-delete" :title="$t('scene.deleteCollider')" @click.stop="store.removeCollider(collider.id)">✕</button>
<button class="scene-object-delete" data-matomo-event="scene.delete-collider" :title="$t('scene.deleteCollider')" @click.stop="store.removeCollider(collider.id)">✕</button>
</div>
<div
v-for="path in store.paths"
@@ -54,11 +54,11 @@
>
<span class="tdot path-dot">◇</span>
<span class="tlabel">{{ path.name }}</span>
<button class="scene-object-eye" :class="{ off: path.visible === false }" :title="path.visible === false ? $t('scene.showPath') : $t('scene.hidePath')" :aria-label="path.visible === false ? $t('scene.showPath') : $t('scene.hidePath')" @click.stop="path.visible = path.visible === false">
<button class="scene-object-eye" data-matomo-event="scene.toggle-path" :class="{ off: path.visible === false }" :title="path.visible === false ? $t('scene.showPath') : $t('scene.hidePath')" :aria-label="path.visible === false ? $t('scene.showPath') : $t('scene.hidePath')" @click.stop="path.visible = path.visible === false">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="path.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
</button>
<span class="scene-object-kind">{{ $t('scene.path') }}</span>
<button class="scene-object-delete" :title="$t('scene.deletePath')" @click.stop="store.removePath(path.id)">✕</button>
<button class="scene-object-delete" data-matomo-event="scene.delete-path" :title="$t('scene.deletePath')" @click.stop="store.removePath(path.id)">✕</button>
</div>
<div
v-for="spine in store.spines"
@@ -69,11 +69,11 @@
>
<span class="tdot spine-dot">◆</span>
<span class="tlabel">{{ spine.name }}</span>
<button class="scene-object-eye" :class="{ off: spine.visible === false }" :title="spine.visible === false ? $t('scene.showSpine') : $t('scene.hideSpine')" :aria-label="spine.visible === false ? $t('scene.showSpine') : $t('scene.hideSpine')" @click.stop="spine.visible = spine.visible === false">
<button class="scene-object-eye" data-matomo-event="scene.toggle-spine" :class="{ off: spine.visible === false }" :title="spine.visible === false ? $t('scene.showSpine') : $t('scene.hideSpine')" :aria-label="spine.visible === false ? $t('scene.showSpine') : $t('scene.hideSpine')" @click.stop="spine.visible = spine.visible === false">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="spine.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
</button>
<span class="scene-object-kind">Spine</span>
<button class="scene-object-delete" :title="$t('scene.deleteSpine')" @click.stop="store.removeSpine(spine.id)">✕</button>
<button class="scene-object-delete" data-matomo-event="scene.delete-spine" :title="$t('scene.deleteSpine')" @click.stop="store.removeSpine(spine.id)">✕</button>
</div>
<div v-if="!store.systems.length && !store.colliders.length && !store.paths.length && !store.spines.length" class="empty">{{ $t('scene.empty') }}</div>
</div>
@@ -89,7 +89,7 @@
<div class="seed-row scene-seed">
<label for="system-seed">{{ $t('scene.seed') }}</label>
<input id="system-seed" class="seed-input" type="number" min="0" step="1" v-model.number="sys.config.seed" :title="$t('scene.seedTitle')" />
<button class="seed-random" :title="$t('scene.randomizeSeed')" @click="randomizeSeed">⚄</button>
<button class="seed-random" data-matomo-event="scene.randomize-seed" :title="$t('scene.randomizeSeed')" @click="randomizeSeed">⚄</button>
</div>
<div class="seed-hint">{{ $t('scene.seedHint') }}</div>
</template>