S6: refactor UI to shadcn components, design system enforcement

- App: use Sheet for mobile sidebar (proper shadcn component)
- ComposeBox: use ToggleGroup + Button + Input (no raw HTML)
- Use Tailwind text scale (text-xs, text-sm) instead of arbitrary text-[10px]
- Design system rule expanded with color palette, forbidden patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 19:56:44 +02:00
parent 791d7e2f69
commit af183abc42
7 changed files with 514 additions and 96 deletions

View File

@@ -1,6 +1,9 @@
import { useState } from "react";
import type { MessageType } from "@/types/MessageType";
import { postMessage } from "@/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
interface Props {
channelId: string;
@@ -9,12 +12,12 @@ interface Props {
onMessageSent: () => void;
}
const TYPES: { value: MessageType; label: string; shortLabel: string }[] = [
{ value: "text", label: "TXT", shortLabel: "T" },
{ value: "code", label: "CODE", shortLabel: "C" },
{ value: "result", label: "RES", shortLabel: "R" },
{ value: "error", label: "ERR", shortLabel: "E" },
{ value: "plan", label: "PLAN", shortLabel: "P" },
const MSG_TYPES: { value: MessageType; label: string }[] = [
{ value: "text", label: "T" },
{ value: "code", label: "C" },
{ value: "result", label: "R" },
{ value: "error", label: "E" },
{ value: "plan", label: "P" },
];
export function ComposeBox({
@@ -48,42 +51,33 @@ export function ComposeBox({
return (
<div className="border-t border-border bg-card px-3 py-2 md:px-4 md:py-3 pb-[env(safe-area-inset-bottom,8px)]">
{replyTo && (
<div className="flex items-center gap-2 mb-1.5 text-[10px] md:text-[11px] text-muted-foreground">
<div className="flex items-center gap-2 mb-1.5 text-xs text-muted-foreground">
<span>^ #{replyTo.slice(0, 8)}</span>
<button
type="button"
onClick={onClearReply}
className="hover:text-foreground min-w-[44px] min-h-[32px] flex items-center justify-center md:min-w-0 md:min-h-0"
>
<Button variant="ghost" size="sm" onClick={onClearReply} className="h-6 px-1 text-xs">
[x]
</button>
</Button>
</div>
)}
<div className="flex items-center gap-1.5 md:gap-2">
{/* Type selector — compact on mobile */}
<div className="flex gap-0.5">
{TYPES.map((t) => (
<button
type="button"
<ToggleGroup
type="single"
value={msgType}
onValueChange={(v) => { if (v) setMsgType(v as MessageType); }}
className="gap-0.5"
>
{MSG_TYPES.map((t) => (
<ToggleGroupItem
key={t.value}
onClick={() => setMsgType(t.value)}
className={`px-1 md:px-1.5 py-1 md:py-0.5 text-[9px] md:text-[10px] font-bold rounded-sm transition-colors min-w-[28px] md:min-w-0 min-h-[36px] md:min-h-0 flex items-center justify-center ${
msgType === t.value
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:text-foreground"
}`}
value={t.value}
className="h-8 w-8 md:h-7 md:w-auto md:px-2 text-xs font-bold data-[state=on]:bg-primary data-[state=on]:text-primary-foreground"
>
{/* Short label on mobile, full on desktop */}
<span className="md:hidden">{t.shortLabel}</span>
<span className="hidden md:inline">{t.label}</span>
</button>
{t.label}
</ToggleGroupItem>
))}
</div>
</ToggleGroup>
{/* Input — larger touch target on mobile */}
<input
type="text"
<Input
value={content}
onChange={(e) => setContent(e.target.value)}
onKeyDown={(e) => {
@@ -91,23 +85,20 @@ export function ComposeBox({
e.preventDefault();
handleSend();
}
if (e.altKey && e.key >= "1" && e.key <= "5") {
setMsgType(TYPES[parseInt(e.key) - 1].value);
}
}}
placeholder="message..."
disabled={sending}
className="flex-1 bg-input text-[13px] md:text-[13px] text-foreground placeholder:text-muted-foreground px-3 py-2 md:py-1.5 rounded-sm border border-border focus:outline-none focus:border-blue-500/30"
className="flex-1 h-9 md:h-8 text-sm"
/>
<button
type="button"
<Button
onClick={handleSend}
disabled={sending || !content.trim()}
className="px-3 py-2 md:py-1.5 text-[11px] font-bold bg-primary text-primary-foreground rounded-sm hover:opacity-80 disabled:opacity-30 transition-opacity min-w-[44px] min-h-[36px] md:min-h-0 flex items-center justify-center"
size="sm"
className="h-9 md:h-8 px-3 text-xs font-bold"
>
{sending ? "..." : "SEND"}
</button>
</Button>
</div>
</div>
);