From 040d973fdf2083be2c52a711dfb8c0acbd93ae61 Mon Sep 17 00:00:00 2001 From: oliver Date: Tue, 8 Sep 2026 17:13:41 +0800 Subject: [PATCH] Give super-admin cron list a three-level tree with leading dots. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User folders stay top-level; nested jobs and run sessions indent with ··· leads so the hierarchy matches workspace session rows. Co-authored-by: Cursor --- lib/client.js | 56 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/client.js b/lib/client.js index 57f8374..8a8e81d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -290,6 +290,10 @@ window.__ModuleLoader__.load({ .dsh-ct-projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex} .dsh-ct-title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden} .dsh-ct-session .dsh-ct-title{flex:1;margin:0 6px 0 4px} +.dsh-ct-treeLead{width:16px;height:20px;flex:none;justify-content:center;align-items:center;display:inline-flex;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:20px;letter-spacing:0.5px;opacity:.72;user-select:none} +.dsh-ct-depth1{padding-left:20px!important} +.dsh-ct-depth2{padding-left:36px!important} +.dsh-ct-ownerChild{margin-left:0} .dsh-ct-time{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:20px} .dsh-ct-rowActs{flex:none;align-items:center;gap:12px;display:none;height:20px} .dsh-ct-project:hover .dsh-ct-rowActs,.dsh-ct-session:hover .dsh-ct-rowActs{display:inline-flex} @@ -736,21 +740,47 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba return name && name !== key ? `${key} · ${name}` : key } - function renderJobGroup(job) { + function treeLead() { + return h('span', { className: 'dsh-ct-treeLead', 'aria-hidden': 'true' }, '\u00b7\u00b7\u00b7') + } + + function renderRunRows(job, depth) { + const jobRuns = runs.filter((run) => run.jobId === job.id) + return jobRuns.map((run) => { + const selectedRun = selection?.type === 'run' && selection.runId === run.id + return h('div', { + key: run.id, + className: joinClass(skin.session, depth >= 2 ? 'dsh-ct-depth2' : 'dsh-ct-depth1'), + role: 'treeitem', + 'data-on': selectedRun ? 'true' : 'false', + onClick: () => onSelectRun(job.id, run), + }, + treeLead(), + h('span', { className: skin.slot }, sessionIcon() || '·'), + h('span', { className: skin.title }, (run.summary || run.status || '').split('\n')[0] || run.status), + h('span', { className: skin.time }, formatTime(run.actualAt || run.scheduledAt, job.schedule?.timezone)), + ) + }) + } + + // depth 0: top-level job (normal users). depth 1: job under a user folder (super/fallback). + function renderJobGroup(job, depth = 0) { const open = expanded[job.id] === true || !!needle const paused = job.enabled === false - const jobRuns = runs.filter((run) => run.jobId === job.id) const selectedJob = selection?.type === 'job' && selection.jobId === job.id const containsCurrent = selectedJob || (selection?.type === 'run' && selection.jobId === job.id) - return h('div', { className: skin.group, key: job.id }, + const nested = depth > 0 + return h('div', { className: joinClass(skin.group, nested && 'dsh-ct-ownerChild'), key: job.id }, h('div', { - className: skin.project, + className: joinClass(skin.project, nested && 'dsh-ct-depth1'), role: 'treeitem', 'aria-expanded': open, + 'aria-level': nested ? 2 : 1, 'data-on': selectedJob ? 'true' : 'false', 'data-paused': paused ? 'true' : 'false', onClick: () => onSelectJob(job.id), }, + nested ? treeLead() : null, h('span', { className: joinClass(skin.slot, skin.folder, open && containsCurrent ? skin.folderOn : ''), 'data-on': open && containsCurrent ? 'true' : 'false', @@ -782,20 +812,7 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba ), h('div', { className: 'dsh-ct-runs', 'data-open': open ? 'true' : 'false' }, h('div', { className: 'dsh-ct-runsInner' }, - jobRuns.map((run) => { - const selectedRun = selection?.type === 'run' && selection.runId === run.id - return h('div', { - key: run.id, - className: skin.session, - role: 'treeitem', - 'data-on': selectedRun ? 'true' : 'false', - onClick: () => onSelectRun(job.id, run), - }, - h('span', { className: skin.slot }, sessionIcon() || '·'), - h('span', { className: skin.title }, (run.summary || run.status || '').split('\n')[0] || run.status), - h('span', { className: skin.time }, formatTime(run.actualAt || run.scheduledAt, job.schedule?.timezone)), - ) - }), + renderRunRows(job, nested ? 2 : 1), ), ), ) @@ -825,6 +842,7 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba className: skin.project, role: 'treeitem', 'aria-expanded': open, + 'aria-level': 1, 'data-on': containsCurrent ? 'true' : 'false', onClick: () => onToggleGroup(folderId), }, @@ -845,7 +863,7 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba ), h('div', { className: 'dsh-ct-runs', 'data-open': open ? 'true' : 'false' }, h('div', { className: 'dsh-ct-runsInner' }, - group.jobs.map((job) => renderJobGroup(job)), + group.jobs.map((job) => renderJobGroup(job, 1)), ), ), )