Keep bot messages inside the column on narrow screens
Lists no longer add twui's 25px list margin on top of full width, markdown blocks and list items are capped at the column, long words and inline code wrap anywhere, and ref chips with long labels truncate instead of pushing the line past the edge. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -42,7 +42,7 @@ export const MessageMarkdownComponents: Components = {
|
|||||||
</MarkdownList>
|
</MarkdownList>
|
||||||
),
|
),
|
||||||
code: ({ children }) => (
|
code: ({ children }) => (
|
||||||
<Span className="font-mono text-[13px] px-1 py-px rounded-default-xs bg-sunken text-ink break-words">
|
<Span className="font-mono text-[13px] px-1 py-px rounded-default-xs bg-sunken text-ink wrap-anywhere">
|
||||||
{children}
|
{children}
|
||||||
</Span>
|
</Span>
|
||||||
),
|
),
|
||||||
@@ -58,7 +58,7 @@ export const MessageMarkdownComponents: Components = {
|
|||||||
),
|
),
|
||||||
table: ({ children }) => (
|
table: ({ children }) => (
|
||||||
<Stack className="w-full max-w-full overflow-x-auto items-start">
|
<Stack className="w-full max-w-full overflow-x-auto items-start">
|
||||||
<table className="text-[13px] leading-5 text-ink border-collapse">{children}</table>
|
<table className="text-[13px] leading-5 text-ink border-collapse wrap-break-word">{children}</table>
|
||||||
</Stack>
|
</Stack>
|
||||||
),
|
),
|
||||||
th: ({ children }) => (
|
th: ({ children }) => (
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const UserMarkdownComponents: Components = {
|
|||||||
</MarkdownList>
|
</MarkdownList>
|
||||||
),
|
),
|
||||||
code: ({ children }) => (
|
code: ({ children }) => (
|
||||||
<Span className="font-mono text-[13px] px-1 py-px rounded-default-xs bg-on-ink/15 text-on-ink break-words">
|
<Span className="font-mono text-[13px] px-1 py-px rounded-default-xs bg-on-ink/15 text-on-ink wrap-anywhere">
|
||||||
{children}
|
{children}
|
||||||
</Span>
|
</Span>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ function BotMessageLine({ message, bot_name, at }: Props) {
|
|||||||
) : null}
|
) : null}
|
||||||
<Stack
|
<Stack
|
||||||
{...{ [QuotableAttribute]: "" }}
|
{...{ [QuotableAttribute]: "" }}
|
||||||
className="w-full gap-2.5! items-start break-words"
|
className="w-full gap-2.5! items-start wrap-anywhere [&>*]:max-w-full"
|
||||||
>
|
>
|
||||||
<Markdown
|
<Markdown
|
||||||
remarkPlugins={RemarkPlugins}
|
remarkPlugins={RemarkPlugins}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function BotRefChip({ bot_id }: Props) {
|
|||||||
: `/admin/bots/${bot.id}`
|
: `/admin/bots/${bot.id}`
|
||||||
}
|
}
|
||||||
title={`Open ${bot.name}`}
|
title={`Open ${bot.name}`}
|
||||||
className="inline-flex h-6 gap-1.5 items-center px-1.5 align-middle rounded-default-xs border-0 bg-sunken text-[13px] font-bold leading-4 text-ink whitespace-nowrap hover:opacity-80"
|
className="inline-flex h-6 max-w-full gap-1.5 items-center px-1.5 align-middle rounded-default-xs border-0 bg-sunken text-[13px] font-bold leading-4 text-ink whitespace-nowrap hover:opacity-80"
|
||||||
>
|
>
|
||||||
<BotAvatar
|
<BotAvatar
|
||||||
image_media_id={bot.image_media_id}
|
image_media_id={bot.image_media_id}
|
||||||
@@ -43,9 +43,10 @@ export default function BotRefChip({ bot_id }: Props) {
|
|||||||
face={(bot.face || "idle") as BotFace}
|
face={(bot.face || "idle") as BotFace}
|
||||||
label={bot.name || ""}
|
label={bot.name || ""}
|
||||||
size={18}
|
size={18}
|
||||||
|
className="shrink-0"
|
||||||
/>
|
/>
|
||||||
{bot.name}
|
<Span className="min-w-0 truncate">{bot.name}</Span>
|
||||||
{workspace_id && bot.is_admin ? <AdminShield size={13} /> : null}
|
{workspace_id && bot.is_admin ? <AdminShield size={13} className="shrink-0" /> : null}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,14 +38,15 @@ export default function ComputerRefChip({ computer_id }: Props) {
|
|||||||
openComputer({ computer_id: computer.id || 0, mode: "browser" })
|
openComputer({ computer_id: computer.id || 0, mode: "browser" })
|
||||||
}
|
}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold whitespace-nowrap text-ink dark:text-ink",
|
"inline-flex h-6 max-w-full gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold whitespace-nowrap text-ink dark:text-ink",
|
||||||
|
"[&_.twui-button-content-wrapper]:min-w-0",
|
||||||
current
|
current
|
||||||
? "bg-sunken hover:bg-sunken outline-0"
|
? "bg-sunken hover:bg-sunken outline-0"
|
||||||
: "bg-transparent hover:bg-sunken",
|
: "bg-transparent hover:bg-sunken",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Monitor size={14} aria-hidden className="shrink-0" />
|
<Monitor size={14} aria-hidden className="shrink-0" />
|
||||||
{name}
|
<Span className="min-w-0 truncate">{name}</Span>
|
||||||
{current ? <Check size={14} aria-hidden className="shrink-0" /> : null}
|
{current ? <Check size={14} aria-hidden className="shrink-0" /> : null}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type Props = {
|
|||||||
*/
|
*/
|
||||||
function CrewMessageLine({ message }: Props) {
|
function CrewMessageLine({ message }: Props) {
|
||||||
return (
|
return (
|
||||||
<Stack className="w-full gap-2.5! items-start break-words">
|
<Stack className="w-full gap-2.5! items-start wrap-anywhere [&>*]:max-w-full">
|
||||||
<Markdown remarkPlugins={RemarkPlugins} components={MessageMarkdownComponents}>
|
<Markdown remarkPlugins={RemarkPlugins} components={MessageMarkdownComponents}>
|
||||||
{linkComputerRefs({ text: message.text || "" })}
|
{linkComputerRefs({ text: message.text || "" })}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
import Modal from "@moduletrace/twui/elements/Modal";
|
import Modal from "@moduletrace/twui/elements/Modal";
|
||||||
import Button from "@moduletrace/twui/layout/Button";
|
import Button from "@moduletrace/twui/layout/Button";
|
||||||
import H2 from "@moduletrace/twui/layout/H2";
|
import H2 from "@moduletrace/twui/layout/H2";
|
||||||
@@ -43,13 +44,16 @@ export default function HookProposalRefChip({ proposal_id }: Props) {
|
|||||||
color="gray"
|
color="gray"
|
||||||
size="smaller"
|
size="smaller"
|
||||||
title={`Review: ${title}`}
|
title={`Review: ${title}`}
|
||||||
className="inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap"
|
className={twMerge(
|
||||||
|
"inline-flex h-6 max-w-full gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap",
|
||||||
|
"[&_.twui-button-content-wrapper]:min-w-0",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Webhook size={14} aria-hidden className="shrink-0" />
|
<Webhook size={14} aria-hidden className="shrink-0" />
|
||||||
{`${title} · ${status}`}
|
<Span className="min-w-0 truncate">{`${title} · ${status}`}</Span>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
targetWrapperProps={{ className: "inline-flex align-middle" }}
|
targetWrapperProps={{ className: "inline-flex max-w-full align-middle" }}
|
||||||
open={open}
|
open={open}
|
||||||
setOpen={setOpen}
|
setOpen={setOpen}
|
||||||
className="max-w-xl bg-paper p-6"
|
className="max-w-xl bg-paper p-6"
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export default function MarkdownLink({ href, children }: ComponentProps<"a">) {
|
|||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-ink underline underline-offset-2 hover:opacity-80 break-words"
|
className="text-ink underline underline-offset-2 hover:opacity-80 wrap-anywhere"
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function MarkdownList({ children, ordered, start, className }: Pr
|
|||||||
start={start}
|
start={start}
|
||||||
items={items}
|
items={items}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
`w-full gap-1! pl-5 my-0 text-sm leading-5 text-ink ${ordered ? "list-decimal" : "list-disc"}`,
|
`self-stretch gap-1! pl-5 my-0 text-sm leading-5 text-ink [&>li]:max-w-full ${ordered ? "list-decimal" : "list-disc"}`,
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
import Modal from "@moduletrace/twui/elements/Modal";
|
import Modal from "@moduletrace/twui/elements/Modal";
|
||||||
import Button from "@moduletrace/twui/layout/Button";
|
import Button from "@moduletrace/twui/layout/Button";
|
||||||
import H2 from "@moduletrace/twui/layout/H2";
|
import H2 from "@moduletrace/twui/layout/H2";
|
||||||
@@ -47,13 +48,16 @@ export default function ProposalRefChip({ proposal_id }: Props) {
|
|||||||
color="gray"
|
color="gray"
|
||||||
size="smaller"
|
size="smaller"
|
||||||
title={`Review: ${title}`}
|
title={`Review: ${title}`}
|
||||||
className="inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap"
|
className={twMerge(
|
||||||
|
"inline-flex h-6 max-w-full gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap",
|
||||||
|
"[&_.twui-button-content-wrapper]:min-w-0",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Icon size={14} aria-hidden className="shrink-0" />
|
<Icon size={14} aria-hidden className="shrink-0" />
|
||||||
{`${title} · ${status}`}
|
<Span className="min-w-0 truncate">{`${title} · ${status}`}</Span>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
targetWrapperProps={{ className: "inline-flex align-middle" }}
|
targetWrapperProps={{ className: "inline-flex max-w-full align-middle" }}
|
||||||
open={open}
|
open={open}
|
||||||
setOpen={setOpen}
|
setOpen={setOpen}
|
||||||
className="max-w-xl bg-paper p-6"
|
className="max-w-xl bg-paper p-6"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
import Modal from "@moduletrace/twui/elements/Modal";
|
import Modal from "@moduletrace/twui/elements/Modal";
|
||||||
import Button from "@moduletrace/twui/layout/Button";
|
import Button from "@moduletrace/twui/layout/Button";
|
||||||
import H2 from "@moduletrace/twui/layout/H2";
|
import H2 from "@moduletrace/twui/layout/H2";
|
||||||
@@ -46,13 +47,16 @@ export default function SecretRequestRefChip({ request_id }: Props) {
|
|||||||
color="gray"
|
color="gray"
|
||||||
size="smaller"
|
size="smaller"
|
||||||
title={view.status == "pending" ? `Enter ${view.key}` : `${view.key}: ${status}`}
|
title={view.status == "pending" ? `Enter ${view.key}` : `${view.key}: ${status}`}
|
||||||
className="inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap"
|
className={twMerge(
|
||||||
|
"inline-flex h-6 max-w-full gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap",
|
||||||
|
"[&_.twui-button-content-wrapper]:min-w-0",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<KeyRound size={14} aria-hidden className="shrink-0" />
|
<KeyRound size={14} aria-hidden className="shrink-0" />
|
||||||
{`${view.key} · ${status}`}
|
<Span className="min-w-0 truncate">{`${view.key} · ${status}`}</Span>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
targetWrapperProps={{ className: "inline-flex align-middle" }}
|
targetWrapperProps={{ className: "inline-flex max-w-full align-middle" }}
|
||||||
open={open}
|
open={open}
|
||||||
setOpen={setOpen}
|
setOpen={setOpen}
|
||||||
className="max-w-xl bg-paper p-6"
|
className="max-w-xl bg-paper p-6"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
import Button from "@moduletrace/twui/layout/Button";
|
import Button from "@moduletrace/twui/layout/Button";
|
||||||
import Span from "@moduletrace/twui/layout/Span";
|
import Span from "@moduletrace/twui/layout/Span";
|
||||||
import { Hand } from "lucide-react";
|
import { Hand } from "lucide-react";
|
||||||
@@ -34,10 +35,13 @@ export default function TakeOverChip({ computer_id, mode }: Props) {
|
|||||||
size="smaller"
|
size="smaller"
|
||||||
title={`Open ${name} (${mode_title}) and take over`}
|
title={`Open ${name} (${mode_title}) and take over`}
|
||||||
onClick={() => openComputer({ computer_id, mode, take_over: true })}
|
onClick={() => openComputer({ computer_id, mode, take_over: true })}
|
||||||
className="inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap"
|
className={twMerge(
|
||||||
|
"inline-flex h-6 max-w-full gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap",
|
||||||
|
"[&_.twui-button-content-wrapper]:min-w-0",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Hand size={14} aria-hidden className="shrink-0" />
|
<Hand size={14} aria-hidden className="shrink-0" />
|
||||||
{`Take over ${name} · ${mode_title}`}
|
<Span className="min-w-0 truncate">{`Take over ${name} · ${mode_title}`}</Span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default function UserBubble({ text }: { text: string }) {
|
|||||||
{...{ [QuotableAttribute]: "" }}
|
{...{ [QuotableAttribute]: "" }}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"max-w-[80%] min-w-0 gap-2.5! items-stretch rounded-default-lg",
|
"max-w-[80%] min-w-0 gap-2.5! items-stretch rounded-default-lg",
|
||||||
"bg-ink text-on-ink px-4 py-3 text-sm leading-5 break-words",
|
"bg-ink text-on-ink px-4 py-3 text-sm leading-5 wrap-anywhere",
|
||||||
"conversation-user-bubble",
|
"conversation-user-bubble",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function UserMarkdownLink(props: ComponentProps<"a">) {
|
|||||||
href={props.href}
|
href={props.href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-blue-400 dark:text-blue-600 underline underline-offset-2 hover:opacity-80 break-words"
|
className="text-blue-400 dark:text-blue-600 underline underline-offset-2 hover:opacity-80 wrap-anywhere"
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ export default function WorkspaceRefChip({ workspace_id }: Props) {
|
|||||||
<Link
|
<Link
|
||||||
href={`/admin/workspaces/${workspace.id}`}
|
href={`/admin/workspaces/${workspace.id}`}
|
||||||
title={`Open ${workspace.name}`}
|
title={`Open ${workspace.name}`}
|
||||||
className="inline-flex h-6 gap-1.5 items-center px-1.5 align-middle rounded-default-xs border-0 bg-sunken text-[13px] font-bold leading-4 text-ink whitespace-nowrap hover:opacity-80"
|
className="inline-flex h-6 max-w-full gap-1.5 items-center px-1.5 align-middle rounded-default-xs border-0 bg-sunken text-[13px] font-bold leading-4 text-ink whitespace-nowrap hover:opacity-80"
|
||||||
>
|
>
|
||||||
<WorkspaceAvatar image_media_id={workspace.image_media_id} size="xs" />
|
<WorkspaceAvatar image_media_id={workspace.image_media_id} size="xs" />
|
||||||
{workspace.name}
|
<Span className="min-w-0 truncate">{workspace.name}</Span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user