refactor: clean up TeamProvisioningService and enhance TaskCommentsSection UI

- Removed outdated OAuth token warning logic from TeamProvisioningService to streamline authentication handling.
- Updated TeamProvisioningBanner to prevent auto-dismissal of "ready" state, allowing users to inspect logs after rapid provisioning cycles.
- Enhanced TaskCommentsSection UI by integrating MemberBadge for author display and improving comment styling for better visual clarity.

Made-with: Cursor
This commit is contained in:
iliya 2026-03-03 16:46:14 +02:00
parent 16bb2c0b63
commit 0e627c4b3f
3 changed files with 40 additions and 62 deletions

View file

@ -957,17 +957,6 @@ export class TeamProvisioningService {
'Using ANTHROPIC_AUTH_TOKEN (proxy) mapped to ANTHROPIC_API_KEY for `-p` mode.' 'Using ANTHROPIC_AUTH_TOKEN (proxy) mapped to ANTHROPIC_API_KEY for `-p` mode.'
); );
} }
if (authSource === 'claude_code_oauth_token_credentials') {
const source =
process.platform === 'darwin'
? 'macOS Keychain or credentials file'
: `${path.join(getClaudeBasePath(), '.credentials.json')}`;
warnings.push(
`Using OAuth token from ${source}. ` +
'Note: this token may be stale if Claude Code refreshed it in-memory without persisting. ' +
'If auth fails, run `claude setup-token` and export CLAUDE_CODE_OAUTH_TOKEN.'
);
}
const probe = await this.probeClaudeRuntime(claudePath, targetCwd, executionEnv); const probe = await this.probeClaudeRuntime(claudePath, targetCwd, executionEnv);

View file

@ -26,8 +26,6 @@ function findProgressForTeam(
return matching[0] ?? null; return matching[0] ?? null;
} }
const READY_DISMISS_MS = 5_000;
export const TeamProvisioningBanner = ({ export const TeamProvisioningBanner = ({
teamName, teamName,
}: TeamProvisioningBannerProps): React.JSX.Element | null => { }: TeamProvisioningBannerProps): React.JSX.Element | null => {
@ -49,28 +47,9 @@ export const TeamProvisioningBanner = ({
} }
} }
useEffect(() => { // NOTE: we intentionally do NOT auto-dismiss "ready" banners.
if (progress?.state !== 'ready') { // Users frequently need to inspect launch output after fast stop→start cycles,
return; // and auto-dismiss can make it look like no progress/logs were produced.
}
// If we captured any logs/output, keep the banner visible so the user
// can inspect what happened (common for fast stop→start cycles).
if (progress.assistantOutput || progress.cliLogsTail || progress.error) {
return;
}
const timer = window.setTimeout(() => {
setDismissed(true);
}, READY_DISMISS_MS);
return () => {
window.clearTimeout(timer);
};
}, [
progress?.state,
progress?.runId,
progress?.assistantOutput,
progress?.cliLogsTail,
progress?.error,
]);
if (!progress || dismissed) { if (!progress || dismissed) {
return null; return null;

View file

@ -2,9 +2,9 @@ import { useCallback, useMemo, useState } from 'react';
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer'; import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
import { ReplyQuoteBlock } from '@renderer/components/team/activity/ReplyQuoteBlock'; import { ReplyQuoteBlock } from '@renderer/components/team/activity/ReplyQuoteBlock';
import { MemberBadge } from '@renderer/components/team/MemberBadge';
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea'; import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { getTeamColorSet } from '@renderer/constants/teamColors';
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence'; import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
import { useMarkCommentsRead } from '@renderer/hooks/useMarkCommentsRead'; import { useMarkCommentsRead } from '@renderer/hooks/useMarkCommentsRead';
import { useStore } from '@renderer/store'; import { useStore } from '@renderer/store';
@ -14,7 +14,16 @@ import { getModifierKeyName } from '@renderer/utils/keyboardUtils';
import { buildMemberColorMap } from '@renderer/utils/memberHelpers'; import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
import { stripAgentBlocks } from '@shared/constants/agentBlocks'; import { stripAgentBlocks } from '@shared/constants/agentBlocks';
import { formatDistanceToNow } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
import { ChevronDown, ChevronUp, MessageSquare, Reply, Send, X } from 'lucide-react'; import {
CheckCircle2,
ChevronDown,
ChevronUp,
Eye,
MessageSquare,
Reply,
Send,
X,
} from 'lucide-react';
import type { MentionSuggestion } from '@renderer/types/mention'; import type { MentionSuggestion } from '@renderer/types/mention';
import type { ResolvedTeamMember, TaskComment } from '@shared/types'; import type { ResolvedTeamMember, TaskComment } from '@shared/types';
@ -146,19 +155,30 @@ export const TaskCommentsSection = ({
) : null} ) : null}
{visibleComments.map((comment) => ( {visibleComments.map((comment) => (
<div key={comment.id} className="group p-2.5"> <div
key={comment.id}
className={[
'group rounded-md p-2.5',
comment.type === 'review_approved'
? 'border border-emerald-500/20 bg-emerald-500/5'
: comment.type === 'review_request'
? 'border border-blue-500/20 bg-blue-500/5'
: '',
].join(' ')}
>
<div className="mb-1 flex items-center gap-2 text-[10px] text-[var(--color-text-muted)]"> <div className="mb-1 flex items-center gap-2 text-[10px] text-[var(--color-text-muted)]">
<span <MemberBadge name={comment.author} color={colorMap.get(comment.author)} />
className="font-medium" {comment.type === 'review_approved' ? (
style={{ <span className="inline-flex items-center gap-0.5 rounded-full bg-emerald-500/15 px-1.5 py-0.5 text-[10px] font-medium text-emerald-400">
color: (() => { <CheckCircle2 size={10} />
const rc = colorMap.get(comment.author); Approved
return rc ? getTeamColorSet(rc).text : 'var(--color-text-secondary)'; </span>
})(), ) : comment.type === 'review_request' ? (
}} <span className="inline-flex items-center gap-0.5 rounded-full bg-blue-500/15 px-1.5 py-0.5 text-[10px] font-medium text-blue-400">
> <Eye size={10} />
{comment.author} Review requested
</span> </span>
) : null}
<span> <span>
{(() => { {(() => {
const date = new Date(comment.createdAt); const date = new Date(comment.createdAt);
@ -290,19 +310,9 @@ export const TaskCommentsSection = ({
{replyTo ? ( {replyTo ? (
<div className="mb-2 flex items-start gap-2 rounded-md border border-[var(--color-border)] bg-[var(--color-surface-raised)] p-2"> <div className="mb-2 flex items-start gap-2 rounded-md border border-[var(--color-border)] bg-[var(--color-surface-raised)] p-2">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="mb-0.5 text-[10px] font-medium text-[var(--color-text-muted)]"> <div className="mb-0.5 flex items-center gap-1 text-[10px] font-medium text-[var(--color-text-muted)]">
Replying to{' '} Replying to
<span <MemberBadge name={replyTo.author} color={colorMap.get(replyTo.author)} />
className="font-semibold"
style={{
color: (() => {
const rc = colorMap.get(replyTo.author);
return rc ? getTeamColorSet(rc).text : 'var(--color-text-secondary)';
})(),
}}
>
@{replyTo.author}
</span>
</div> </div>
<div className="line-clamp-3 text-[11px] text-[var(--color-text-muted)]"> <div className="line-clamp-3 text-[11px] text-[var(--color-text-muted)]">
{replyTo.text} {replyTo.text}