Give super-admin cron list a three-level tree with leading dots.

User folders stay top-level; nested jobs and run sessions indent with ··· leads so the hierarchy matches workspace session rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-08 17:13:41 +08:00
parent d1127dba88
commit 040d973fdf

View file

@ -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)),
),
),
)