程序员阿江(Relakkes) 993b96cd39 Stabilize the desktop transcript so long agent sessions stay readable
The desktop app now keeps the composer stable while turns are active,
reduces low-signal tool noise in the transcript, restores project context
under the composer after session creation, and relies on the CLI's own
permission requests instead of injecting broader desktop-side Bash asks.

This also brings in the supporting desktop app source tree and the server
routes/session metadata needed for git info, filesystem browsing, session
resume, slash commands, and SDK-backed permission bridging so the UI can
operate as a coherent feature instead of a partial patch.

Constraint: Desktop transcript needs to stay usable during long multi-tool sessions without hiding file-change diffs
Constraint: Permission prompts must mirror CLI behavior closely enough that read-only commands do not get desktop-only prompts
Rejected: Keep rendering Read/Bash bodies inline | too noisy and unlike the intended transcript model
Rejected: Commit only the touched desktop files | would leave the newly introduced desktop app incomplete in git history
Confidence: medium
Scope-risk: broad
Reversibility: messy
Directive: Treat non-writing tools as summary-first transcript events; do not re-expand them by default without validating the UX against long sessions
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run
Tested: bun test src/server/__tests__/conversations.test.ts
Not-tested: Manual visual regression against the exact screenshots in a live desktop session
Not-tested: Full root TypeScript check (repository still has unrelated extracted-native parse failures)
2026-04-06 20:37:44 +08:00

39 lines
1.3 KiB
TypeScript

import type { TextareaHTMLAttributes } from 'react'
type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
label?: string
error?: string
required?: boolean
}
export function Textarea({ label, error, required, className = '', id, ...props }: TextareaProps) {
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-')
return (
<div className="flex flex-col gap-1">
{label && (
<label htmlFor={inputId} className="text-sm font-medium text-[var(--color-text-primary)]">
{label}
{required && <span className="text-[var(--color-error)] ml-0.5">*</span>}
</label>
)}
<textarea
id={inputId}
className={`
min-h-[120px] px-3 py-2 rounded-[var(--radius-lg)] border text-sm resize-y
bg-[var(--color-surface)] text-[var(--color-text-primary)]
placeholder:text-[var(--color-text-tertiary)]
transition-colors duration-150
${error
? 'border-[var(--color-error)]'
: 'border-[var(--color-border)] focus:border-[var(--color-border-focus)] focus:shadow-[0_0_0_3px_rgba(153,153,153,0.1)]'
}
outline-none
${className}
`}
{...props}
/>
{error && <p className="text-xs text-[var(--color-error)]">{error}</p>}
</div>
)
}