mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
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)
39 lines
1.3 KiB
TypeScript
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>
|
|
)
|
|
}
|