style(web): polish subagent and bash chrome
This commit is contained in:
@@ -55,6 +55,9 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.menu > .node {
|
||||||
|
margin-left: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -64,7 +67,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
padding: 7px 8px 7px 3px;
|
padding: 7px 8px 7px 11px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -76,12 +79,26 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row:hover,
|
.row:hover > .clickarea,
|
||||||
.row:focus-visible {
|
.row:focus-visible > .clickarea {
|
||||||
background: var(--dsw-alias-interactive-bg-hover);
|
background: var(--dsw-alias-interactive-bg-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row > :global([data-state]) {
|
.clickarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-self: stretch;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
margin: -7px -8px -7px;
|
||||||
|
padding: 7px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row > :global([data-state]),
|
||||||
|
.clickarea > :global([data-state]) {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +171,7 @@
|
|||||||
|
|
||||||
.children {
|
.children {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: 10px;
|
margin-left: 18px;
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +188,10 @@
|
|||||||
height: 26px;
|
height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.children[aria-busy='true']::before {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
.children > .node::before {
|
.children > .node::before {
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -179,15 +200,15 @@
|
|||||||
|
|
||||||
.children > .node:last-child::before {
|
.children > .node:last-child::before {
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
height: 25px;
|
height: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.children > .node > .row::before {
|
.children > .node > .row::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 24px;
|
top: 16px;
|
||||||
left: -4px;
|
left: -4px;
|
||||||
width: 7px;
|
width: 14px;
|
||||||
border-top: 1px solid var(--dsw-alias-border-l2);
|
border-top: 1px solid var(--dsw-alias-border-l2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ function CatalogRows({
|
|||||||
const childCatalog = catalogs[entry.id]
|
const childCatalog = catalogs[entry.id]
|
||||||
const isExpanded = expanded.has(entry.id)
|
const isExpanded = expanded.has(entry.id)
|
||||||
const knownLeaf = !entry.hasChildren
|
const knownLeaf = !entry.hasChildren
|
||||||
|
const emptyLoading = childCatalog?.state === 'loading' && childCatalog.entries.length === 0
|
||||||
const summary = summaries[entry.id]
|
const summary = summaries[entry.id]
|
||||||
const label = entry.label ?? entry.id
|
const label = entry.label ?? entry.id
|
||||||
const mode = entry.mode === 'one-shot' ? '一次性' : '可继续'
|
const mode = entry.mode === 'one-shot' ? '一次性' : '可继续'
|
||||||
@@ -176,15 +177,21 @@ function CatalogRows({
|
|||||||
<IconChevronRightOutline14 />
|
<IconChevronRightOutline14 />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<StateDot state={entry.activity === 'running' ? 'ongoing' : 'done'} />
|
<div className={css.clickarea}>
|
||||||
<span className={css.content}>
|
<StateDot state={entry.activity === 'running' ? 'ongoing' : 'done'} />
|
||||||
<span className={css.label}>{label}</span>
|
<span className={css.content}>
|
||||||
<span className={css.summary}>{secondary}</span>
|
<span className={css.label}>{label}</span>
|
||||||
</span>
|
<span className={css.summary}>{secondary}</span>
|
||||||
{time !== undefined && <span className={css.time}>{time}</span>}
|
</span>
|
||||||
|
{time !== undefined && <span className={css.time}>{time}</span>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isExpanded && childCatalog !== undefined && !knownLeaf && (
|
{isExpanded && childCatalog !== undefined && !knownLeaf && (
|
||||||
<div role="group" className={css.children}>
|
<div
|
||||||
|
role="group"
|
||||||
|
className={css.children}
|
||||||
|
aria-busy={emptyLoading || undefined}
|
||||||
|
>
|
||||||
<CatalogRows
|
<CatalogRows
|
||||||
parentSessionId={entry.id}
|
parentSessionId={entry.id}
|
||||||
catalog={childCatalog}
|
catalog={childCatalog}
|
||||||
|
|||||||
@@ -237,6 +237,31 @@ describe('SubagentCatalogAction', () => {
|
|||||||
expect(input.setCatalogOpen).toHaveBeenCalledWith(CHILD, false)
|
expect(input.setCatalogOpen).toHaveBeenCalledWith(CHILD, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows initial descendant loading status without drawing its branch', () => {
|
||||||
|
const loading = props(catalog(), {
|
||||||
|
[CHILD]: catalog({ entries: [], state: 'loading' }),
|
||||||
|
})
|
||||||
|
const view = render(<SubagentCatalogAction {...loading} />)
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /2 个子代理/ }))
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '展开 worker 的下级子代理' }))
|
||||||
|
|
||||||
|
expect(loading.setCatalogOpen).toHaveBeenCalledWith(CHILD, true)
|
||||||
|
expect(screen.getByRole('group').getAttribute('aria-busy')).toBe('true')
|
||||||
|
expect(screen.getByText('正在加载子代理…')).toBeTruthy()
|
||||||
|
|
||||||
|
const ready = props(catalog(), {
|
||||||
|
[CHILD]: catalog({
|
||||||
|
entries: [{
|
||||||
|
kind: 'child', id: GRANDCHILD, mode: 'continuable',
|
||||||
|
label: 'indexer', activity: 'inactive', hasChildren: false,
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
view.rerender(<SubagentCatalogAction {...ready} />)
|
||||||
|
expect(screen.getByRole('group').getAttribute('aria-busy')).toBeNull()
|
||||||
|
expect(screen.getByRole('treeitem', { name: /indexer/ })).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
it('uses ArrowRight and ArrowLeft for branch disclosure', async () => {
|
it('uses ArrowRight and ArrowLeft for branch disclosure', async () => {
|
||||||
const input = props(catalog(), {
|
const input = props(catalog(), {
|
||||||
[CHILD]: catalog({
|
[CHILD]: catalog({
|
||||||
|
|||||||
Reference in New Issue
Block a user