Compare commits

...

1 Commits

Author SHA1 Message Date
Aarav Sareen 991a93840b feat(app): distinguish attention tab indicators 2026-09-01 22:11:03 +05:30
7 changed files with 42 additions and 6 deletions
@@ -356,7 +356,12 @@ export function HomeSessionStatusController(props: {
server: ServerConnection.Key
record: HomeSessionRecord
isOpenTab: (record: HomeSessionRecord) => boolean
render: (state: { unread: Accessor<boolean>; loading: Accessor<boolean>; open: Accessor<boolean> }) => JSX.Element
render: (state: {
unread: Accessor<boolean>
attention: Accessor<boolean>
loading: Accessor<boolean>
open: Accessor<boolean>
}) => JSX.Element
}) {
const avatar = useSessionTabAvatarState(
() => props.server,
@@ -365,6 +370,7 @@ export function HomeSessionStatusController(props: {
)
return props.render({
unread: avatar.unread,
attention: avatar.attention,
loading: avatar.loading,
open: () => props.isOpenTab(props.record),
})
+3
View File
@@ -193,6 +193,7 @@ function HomeSessionLeadingController(props: {
revealProjectOnHover={props.revealProjectOnHover}
open={state.open()}
unread={state.unread()}
attention={state.attention()}
loading={state.loading()}
/>
)}
@@ -205,6 +206,7 @@ function HomeSessionLeading(props: {
revealProjectOnHover: boolean
open: boolean
unread: boolean
attention: boolean
loading: boolean
}) {
return (
@@ -224,6 +226,7 @@ function HomeSessionLeading(props: {
directory={props.record.session.location.directory}
revealProjectOnHover={props.revealProjectOnHover}
unread={props.unread}
attention={props.attention}
loading={props.loading}
/>
</div>
@@ -44,5 +44,5 @@ export function useSessionTabAvatarState(
if (needsAttention()) return false
return data.session.status(sessionId()) === "running"
})
return { unread, loading }
return { unread, attention: needsAttention, loading }
}
@@ -23,6 +23,7 @@ export function SessionTabAvatar(props: {
directory={props.directory}
revealProjectOnHover={props.revealProjectOnHover}
unread={state.unread()}
attention={state.attention()}
loading={state.loading()}
/>
)
@@ -33,6 +34,7 @@ export function SessionTabAvatarView(props: {
directory: string
revealProjectOnHover?: boolean
unread: boolean
attention: boolean
loading: boolean
}) {
const projectAvatar = () => (
@@ -41,6 +43,7 @@ export function SessionTabAvatarView(props: {
src={getProjectAvatarSource(props.project?.id, props.project?.icon)}
variant={getProjectAvatarVariant(props.project?.icon?.color)}
unread={props.unread}
attention={props.attention}
/>
)
return (
@@ -121,6 +121,10 @@
pointer-events: none;
}
[data-component="project-avatar-v2"][data-attention] [data-slot="project-avatar-unread-dot"] {
background: var(--v2-orange-600);
}
[data-slot="project-avatar-slot"] {
display: flex;
flex-shrink: 0;
@@ -7,13 +7,14 @@ Saturated 16px project avatar with color variants and optional unread dot.
### API
- Required: \`fallback\` string.
- Optional: \`src\`, \`variant\`, \`unread\`.
- Optional: \`src\`, \`variant\`, \`unread\`, \`attention\`.
### Variants
- Color: orange, yellow, cyan, green, red, pink, blue, purple, gray.
- Outline: neutral, muted style for de-emphasized projects (e.g. recently closed).
- Image vs initial content state.
- Unread dot with corner mask when \`unread\` is set.
- Orange attention dot when \`attention\` is set.
### Theming
- Uses \`--v2-avatar-bg-*\` and \`--v2-avatar-border-*\` tokens with inset box-shadow borders.
@@ -78,6 +79,14 @@ export const Unread = {
},
}
export const Attention = {
args: {
fallback: "O",
variant: "orange",
attention: true,
},
}
export const AllVariantsUnread = {
render: () => (
<div style={{ display: "flex", gap: "16px", "align-items": "center" }}>
@@ -34,15 +34,26 @@ export interface ProjectAvatarProps extends ComponentProps<"div"> {
src?: string
variant?: ProjectAvatarStyle
unread?: boolean
attention?: boolean
}
export function ProjectAvatar(props: ProjectAvatarProps) {
const [split, rest] = splitProps(props, ["fallback", "src", "variant", "unread", "class", "classList", "style"])
const [split, rest] = splitProps(props, [
"fallback",
"src",
"variant",
"unread",
"attention",
"class",
"classList",
"style",
])
return (
<div
{...rest}
data-component="project-avatar-v2"
data-unread={split.unread ? "" : undefined}
data-unread={split.unread || split.attention ? "" : undefined}
data-attention={split.attention ? "" : undefined}
classList={{
...split.classList,
[split.class ?? ""]: !!split.class,
@@ -58,7 +69,7 @@ export function ProjectAvatar(props: ProjectAvatarProps) {
{(value) => <img src={value()} draggable={false} data-slot="project-avatar-image" />}
</Show>
</div>
<Show when={split.unread}>
<Show when={split.unread || split.attention}>
<span data-slot="project-avatar-unread-dot" aria-hidden="true" />
</Show>
</div>