feat: add scene thumbnails to TreeFlow nodes with auto-generated demo thumbs
This commit is contained in:
@@ -205,6 +205,7 @@ const tree = computed(() => buildTreeForChapter(currentChapterId.value))
|
||||
<TreeFlow
|
||||
v-if="tree"
|
||||
:node="tree"
|
||||
:scenes="scenes"
|
||||
:key="currentChapterId"
|
||||
@select-scene="onSelectScene"
|
||||
/>
|
||||
|
||||
@@ -5,6 +5,7 @@ import dagre from 'dagre'
|
||||
|
||||
const props = defineProps<{
|
||||
node: PlayerTreeNode | null
|
||||
scenes?: Record<string, { thumbnail?: string }>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -15,6 +16,7 @@ interface FlowNode {
|
||||
id: string
|
||||
sceneId: string
|
||||
label: string
|
||||
thumbnail?: string
|
||||
visited: boolean
|
||||
isMystery: boolean
|
||||
locked: boolean
|
||||
@@ -38,7 +40,7 @@ const containerW = ref(800)
|
||||
const containerH = ref(400)
|
||||
|
||||
function buildFlow(root: PlayerTreeNode) {
|
||||
const dagreNodes: { id: string; sceneId: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; locked: boolean; lockHint?: string }[] = []
|
||||
const dagreNodes: { id: string; sceneId: string; thumbnail?: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; locked: boolean; lockHint?: string }[] = []
|
||||
const dagreEdges: { from: string; to: string; visited: boolean }[] = []
|
||||
|
||||
function walk(node: PlayerTreeNode, parentId: string | null) {
|
||||
@@ -47,6 +49,7 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
dagreNodes.push({
|
||||
id: dagreId,
|
||||
sceneId: node.sceneId,
|
||||
thumbnail: props.scenes?.[node.sceneId]?.thumbnail,
|
||||
parent: parentId,
|
||||
label: node.label,
|
||||
visited: true,
|
||||
@@ -91,11 +94,13 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
g.setGraph({ rankdir: 'LR', nodesep: 40, ranksep: 80, marginx: 24, marginy: 24 })
|
||||
g.setDefaultEdgeLabel(() => ({}))
|
||||
|
||||
const nodeW = 120
|
||||
const nodeH = 44
|
||||
const baseW = 128
|
||||
const baseH = 40
|
||||
const thumbH = 78
|
||||
|
||||
for (const n of dagreNodes) {
|
||||
g.setNode(n.id, { width: nodeW, height: nodeH })
|
||||
const hasThumb = !!n.thumbnail
|
||||
g.setNode(n.id, { width: hasThumb ? baseW + 20 : baseW, height: hasThumb ? baseH + thumbH + 8 : baseH })
|
||||
}
|
||||
for (const e of dagreEdges) {
|
||||
g.setEdge(e.from, e.to)
|
||||
@@ -105,18 +110,22 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
|
||||
const resultNodes: FlowNode[] = dagreNodes.map((n) => {
|
||||
const pos = g.node(n.id)
|
||||
const hasThumb = !!n.thumbnail
|
||||
const nw = hasThumb ? baseW + 20 : baseW
|
||||
const nh = hasThumb ? baseH + thumbH + 8 : baseH
|
||||
return {
|
||||
id: n.id,
|
||||
sceneId: n.sceneId,
|
||||
thumbnail: n.thumbnail,
|
||||
label: n.label,
|
||||
visited: n.visited,
|
||||
isMystery: n.isMystery,
|
||||
locked: n.locked,
|
||||
lockHint: n.lockHint,
|
||||
x: pos.x - nodeW / 2,
|
||||
y: pos.y - nodeH / 2,
|
||||
w: nodeW,
|
||||
h: nodeH,
|
||||
x: pos.x - nw / 2,
|
||||
y: pos.y - nh / 2,
|
||||
w: nw,
|
||||
h: nh,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -234,6 +243,7 @@ const svgH = computed(() => containerH.value)
|
||||
:title="n.lockHint || ''"
|
||||
@click="n.visited && n.sceneId && emit('selectScene', n.sceneId)"
|
||||
>
|
||||
<img v-if="n.thumbnail" :src="n.thumbnail" class="node-thumb" />
|
||||
<span class="node-icon">{{ n.visited ? '✦' : n.isMystery ? '?' : '⬜' }}</span>
|
||||
<span class="node-label">{{ n.label }}</span>
|
||||
</div>
|
||||
@@ -264,15 +274,29 @@ const svgH = computed(() => containerH.value)
|
||||
.flow-node {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 10px;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
transition: all 0.15s;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flow-node:has(.node-thumb) {
|
||||
padding: 4px 4px 6px;
|
||||
}
|
||||
|
||||
.node-thumb {
|
||||
width: 128px;
|
||||
height: 72px;
|
||||
object-fit: cover;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.flow-node.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user