This commit is contained in:
team3
2026-06-30 00:36:41 +02:00
parent c794fcaccf
commit 3669fd8d0a
6 changed files with 52 additions and 26 deletions

View File

@@ -23,22 +23,32 @@ const phaseGroups = computed(() => {
return out
})
const selected = ref(null) // marked start point (step index)
const startSel = ref(null) // marked start point (step index) — only ≤ current stand
const endSel = ref(null) // optional end point (step index, > start) — generation stops there
const confirm = ref(null) // which destructive action currently shows "Sure?"
const selectedLabel = computed(() => props.steps[selected.value]?.label || '')
const startLabel = computed(() => props.steps[startSel.value]?.label || '')
const endLabel = computed(() => props.steps[endSel.value]?.label || '')
// Start is only valid up to the current stand: done/active steps, never a pending one (never ran).
function startDisabled(idx) { return startSel.value === null && props.steps[idx]?.state === 'pending' }
function inRange(idx) { return startSel.value !== null && endSel.value !== null && idx > startSel.value && idx < endSel.value }
function stepClick(idx) {
if (props.generating) return
selected.value = selected.value === idx ? null : idx
if (props.generating || startDisabled(idx)) return
confirm.value = null
if (startSel.value === null) { startSel.value = idx; endSel.value = null } // 1st click → start
else if (idx === startSel.value) { startSel.value = null; endSel.value = null } // re-click start → clear
else if (idx > startSel.value) { endSel.value = endSel.value === idx ? null : idx } // later step → toggle end
else if (props.steps[idx]?.state !== 'pending') { startSel.value = idx; endSel.value = null } // earlier → new start
}
function clearSel() { startSel.value = null; endSel.value = null; confirm.value = null }
// 2-click confirmation for destructive actions: first click "arms", second runs it.
function arm(action, fn) {
if (confirm.value === action) { confirm.value = null; fn() }
else confirm.value = action
}
function regenerateFromHere() { const i = selected.value; selected.value = null; confirm.value = null; emit('restartFrom', i) }
function deleteFromHere() { const i = selected.value; selected.value = null; confirm.value = null; emit('resetFrom', i) }
function regenerateFromHere() { const from = startSel.value, to = endSel.value; clearSel(); emit('restartFrom', { from, to }) }
function deleteFromHere() { const from = startSel.value; clearSel(); emit('resetFrom', from) }
const items = ref([])
const loading = ref(true)
@@ -119,19 +129,19 @@ const subTotal = computed(() => items.value.reduce((n, b) => n + (b.subblocks?.l
v-for="s in g.steps"
:key="s.idx"
class="bk-step"
:class="[s.state, { sel: selected === s.idx }]"
:disabled="generating"
:title="`Choose start point «${s.label}»`"
:class="[s.state, { sel: startSel === s.idx, end: endSel === s.idx, 'in-range': inRange(s.idx) }]"
:disabled="generating || startDisabled(s.idx)"
:title="startDisabled(s.idx) ? `«${s.label}» — not reached yet` : (startSel !== null && s.idx > startSel ? `End at «${s.label}»` : `Start at «${s.label}»`)"
@click="stepClick(s.idx)"
>{{ s.label }}</button>
</div>
</div>
</div>
<div v-if="selected !== null && !generating" class="bk-step-actions">
<span class="bk-step-actions-label">From «{{ selectedLabel }}»:</span>
<div v-if="startSel !== null && !generating" class="bk-step-actions">
<span class="bk-step-actions-label">From «{{ startLabel }}»<span v-if="endSel !== null"> to «{{ endLabel }}»</span>:</span>
<button class="bk-act play" @click="regenerateFromHere"> regenerate</button>
<button class="bk-act danger" :class="{ armed: confirm === 'reset' }" @click="arm('reset', deleteFromHere)">{{ confirm === 'reset' ? 'Sure?' : ' delete all' }}</button>
<button class="bk-act ghost" @click="selected = null; confirm = null">Cancel</button>
<button class="bk-act ghost" @click="clearSel">Cancel</button>
</div>
</section>
@@ -251,7 +261,10 @@ const subTotal = computed(() => items.value.reduce((n, b) => n + (b.subblocks?.l
.bk-step.done { border-color: var(--success-border); color: var(--success); }
.bk-step.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.bk-step.pending { color: var(--text-faint); }
.bk-step.sel { border-color: var(--accent); color: var(--on-accent); background: var(--accent); font-weight: 700; box-shadow: 0 0 0 2px var(--accent-soft); }
.bk-step.sel,
.bk-step.end { border-color: var(--accent); color: var(--on-accent); background: var(--accent); font-weight: 700; box-shadow: 0 0 0 2px var(--accent-soft); }
.bk-step.in-range { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.bk-step:disabled:not(.done):not(.active) { opacity: 0.45; }
/* Header: progress left, global buttons right */
.bk-steps-top { display: flex; align-items: center; gap: 1rem; min-height: 1.9rem; margin-bottom: 0.7rem; }