added select button while editing guides
Template tests / tests (push) Successful in 1m48s

This commit is contained in:
Iisyourdad
2026-06-11 11:17:24 -05:00
parent 706d9dce60
commit 726f993a7a
2 changed files with 79 additions and 2 deletions
+77 -2
View File
@@ -47,6 +47,8 @@ class GuideEditor {
this.steps = [];
this.stepMap = new Map();
this.selectedStepId = null;
this.stepSelectMode = false;
this.selectedSteps = new Set();
this.selectedAnnotationId = null;
this.currentTool = 'select';
this.currentZoom = 'fit';
@@ -166,10 +168,12 @@ class GuideEditor {
el('div.row', {},
this.dom.addStepBtn = el('button.primary', { type: 'button' }, 'Add'),
this.dom.importBtn = el('button', { type: 'button' }, 'Import'),
this.dom.selectStepsBtn = el('button', { type: 'button' }, 'Select'),
),
),
this.dom.stepsList = el('div.steps-list'),
el('div.pane-foot', {},
this.dom.stepBulkBar = el('div'),
this.dom.paneFoot = el('div.pane-foot', {},
this.dom.moveUpBtn = el('button.icon', { type: 'button', title: 'Move step up' }, '↑'),
this.dom.moveDownBtn = el('button.icon', { type: 'button', title: 'Move step down' }, '↓'),
this.dom.duplicateBtn = el('button', { type: 'button' }, 'Duplicate'),
@@ -289,6 +293,7 @@ class GuideEditor {
bindShellEvents() {
this.dom.addStepBtn.addEventListener('click', () => this.addEmptyStep());
this.dom.importBtn.addEventListener('click', () => this.importImageSteps());
this.dom.selectStepsBtn.addEventListener('click', () => this.toggleStepSelectMode());
this.dom.moveUpBtn.addEventListener('click', () => this.moveSelectedStep(-1));
this.dom.moveDownBtn.addEventListener('click', () => this.moveSelectedStep(1));
this.dom.duplicateBtn.addEventListener('click', () => this.duplicateSelectedStep());
@@ -528,6 +533,7 @@ class GuideEditor {
const numbers = stepNumberMap(this.steps);
clearNode(this.dom.stepsList);
this.dom.stepCount.textContent = `${this.steps.length} step${this.steps.length === 1 ? '' : 's'}`;
this.dom.selectStepsBtn.className = this.stepSelectMode ? 'primary' : '';
for (const step of this.steps) {
const number = numbers.get(step.stepId) || '';
let depth = 0;
@@ -537,12 +543,17 @@ class GuideEditor {
parent = this.stepMap.get(parent).parentStepId;
}
const selected = current && current.stepId === step.stepId;
const checked = this.selectedSteps.has(step.stepId);
const item = el('div.step-item', {
className: `step-item${selected ? ' selected' : ''}${depth ? ' sub' : ''}${step.skipped ? ' skipped' : ''}${step.hidden ? ' hiddenstep' : ''}`,
dataset: { stepId: step.stepId },
onClick: () => this.selectStep(step.stepId),
onClick: () => {
if (this.stepSelectMode) this.toggleStepSelection(step.stepId);
else this.selectStep(step.stepId);
},
onContextMenu: (e) => {
e.preventDefault();
if (this.stepSelectMode) return;
this.selectStep(step.stepId);
contextMenu(e.clientX, e.clientY, [
{ label: 'Add substep', action: () => this.addSubstep(step.stepId) },
@@ -555,6 +566,14 @@ class GuideEditor {
]);
},
},
this.stepSelectMode
? el('input', {
type: 'checkbox',
checked,
onClick: (e) => e.stopPropagation(),
onChange: () => this.toggleStepSelection(step.stepId),
})
: null,
el('span.status-dot', { className: `status-dot status-${step.status}` }),
el('span.num', {}, number || '•'),
el('span.t', {}, step.title || 'Untitled step'),
@@ -568,6 +587,62 @@ class GuideEditor {
if (!this.steps.length) {
this.dom.stepsList.append(el('div.empty-state', { style: { marginTop: '40px' } }, 'No steps yet.'));
}
this.renderStepBulkBar();
}
toggleStepSelectMode() {
this.stepSelectMode = !this.stepSelectMode;
this.selectedSteps = new Set();
this.renderStepList();
}
toggleStepSelection(stepId) {
if (this.selectedSteps.has(stepId)) this.selectedSteps.delete(stepId);
else this.selectedSteps.add(stepId);
this.renderStepList();
}
selectAllSteps() {
this.selectedSteps = new Set(this.steps.map((s) => s.stepId));
this.renderStepList();
}
clearStepSelection() {
this.selectedSteps = new Set();
this.renderStepList();
}
renderStepBulkBar() {
clearNode(this.dom.stepBulkBar);
this.dom.paneFoot.classList.toggle('hidden', this.stepSelectMode);
if (!this.stepSelectMode) return;
const n = this.selectedSteps.size;
const allSelected = this.steps.length > 0 && n === this.steps.length;
this.dom.stepBulkBar.append(
el('div.bulk-bar', {},
el('span', {}, n ? `${n} selected` : 'Select steps to delete'),
el('span.spacer', {}),
el('button', {
type: 'button',
onClick: () => (allSelected ? this.clearStepSelection() : this.selectAllSteps()),
}, allSelected ? 'Clear' : 'Select all'),
el('button.danger', { type: 'button', disabled: !n, onClick: () => this.deleteSelectedSteps() }, 'Delete'),
),
);
}
async deleteSelectedSteps() {
const ids = [...this.selectedSteps];
if (!ids.length) return;
const ok = await confirmDialog(`Delete ${ids.length} step${ids.length === 1 ? '' : 's'}?`, { danger: true, okLabel: 'Delete' });
if (!ok) return;
for (const stepId of ids) {
await api.step.delete({ guideId: this.guideId, stepId });
}
this.stepSelectMode = false;
this.selectedSteps = new Set();
await this.reload(null);
this.onToast(`${ids.length} step${ids.length === 1 ? '' : 's'} deleted.`);
}
syncStepFields() {
+2
View File
@@ -56,6 +56,8 @@ Initial release.
"Open guide" from the right-click menu) now arms a paused capture
session for it, so the red REC bar appears immediately with a "Start
recording" option to resume capturing more steps.
- Editor step list: a "Select" toggle enables multi-select (checkboxes)
with a "Select all" / "Delete" bar for removing several steps at once.
### Fixed