click-to-select for multi-reply + /new command + layout fix

- Click any message to select it for reply (click again to deselect)
- Multiple messages can be selected — compose shows all
- Selected messages get orange left border + tinted bg
- Floating pill shows ✓ when selected
- /new <name> slash command creates channels from compose box
- Proper multi-reply context in compose with count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:36:39 +02:00
parent 518cfc2d95
commit 4a05665d64
3 changed files with 75 additions and 35 deletions

View File

@@ -43,7 +43,7 @@ export default function App() {
const [activeChannelId, setActiveChannelId] = useState<string | null>(null);
const [messages, setMessages] = useState<Message[]>([]);
const [loading, setLoading] = useState(false);
const [replyTo, setReplyTo] = useState<{ id: string; username: string; content: string } | null>(null);
const [selectedMessages, setSelectedMessages] = useState<{ id: string; username: string; content: string }[]>([]);
const [sheetOpen, setSheetOpen] = useState(false);
const [showScrollDown, setShowScrollDown] = useState(false);
const scrollRef = useRef<HTMLDivElement>(null);
@@ -93,7 +93,7 @@ export default function App() {
useEffect(() => {
setMessages([]);
setReplyTo(null);
setSelectedMessages([]);
prevMsgCountRef.current = 0;
loadMessages();
}, [activeChannelId, loadMessages]);
@@ -212,11 +212,15 @@ export default function App() {
message={msg}
replyTarget={msg.reply_to ? messagesById.get(msg.reply_to) : undefined}
currentUsername={getCurrentUsername()}
onReply={(id) => {
const target = messagesById.get(id);
if (target) {
setReplyTo({ id, username: target.user.display_name, content: target.content });
}
selected={selectedMessages.some((s) => s.id === msg.id)}
onSelect={(id) => {
setSelectedMessages((prev) => {
const exists = prev.find((s) => s.id === id);
if (exists) return prev.filter((s) => s.id !== id);
const target = messagesById.get(id);
if (!target) return prev;
return [...prev, { id, username: target.user.display_name, content: target.content }];
});
}}
onDelete={async (chId, msgId) => {
try {
@@ -248,9 +252,11 @@ export default function App() {
<ComposeBox
key={activeChannelId}
channelId={activeChannelId}
replyTo={replyTo}
onClearReply={() => setReplyTo(null)}
replyTo={selectedMessages.length > 0 ? selectedMessages[0] : null}
selectedMessages={selectedMessages}
onClearReply={() => setSelectedMessages([])}
onMessageSent={() => {
setSelectedMessages([]);
loadMessages();
setTimeout(() => scrollToBottom(), 100);
}}