Skip to content

Commit 63c9121

Browse files
authored
Chatbox nits (#1872)
1 parent 3d08688 commit 63c9121

25 files changed

Lines changed: 1207 additions & 1274 deletions

mcpjam-inspector/client/src/components/__tests__/ChatboxesTab.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ vi.mock("../chatboxes/builder/ChatboxBuilderView", () => ({
214214
<h2>Builder view</h2>
215215
<p>Chatbox: {props.chatboxId ?? "new"}</p>
216216
<p>Draft: {props.draft?.name ?? "none"}</p>
217-
<p>Workspace: {props.workspaceName ?? "unknown"}</p>
217+
<p>Workspace: {props.workspaceId ?? "unknown"}</p>
218218
<p>View mode: {props.initialViewMode ?? "none"}</p>
219219
<button type="button" onClick={props.onBack}>
220220
Back to index
@@ -309,7 +309,7 @@ describe("ChatboxesTab", () => {
309309

310310
expect(await screen.findByText("Builder view")).toBeInTheDocument();
311311
expect(screen.getByText("Chatbox: sbx-2")).toBeInTheDocument();
312-
expect(screen.getByText("Workspace: Workspace One")).toBeInTheDocument();
312+
expect(screen.getByText("Workspace: ws-1")).toBeInTheDocument();
313313
});
314314

315315
it("opens the starter launcher from the new chatbox action", async () => {

mcpjam-inspector/client/src/components/chatboxes/ChatboxEditor.tsx

Lines changed: 78 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
Lock,
1111
Save,
1212
Trash2,
13+
Users,
1314
} from "lucide-react";
1415
import { toast } from "sonner";
1516
import {
@@ -18,6 +19,11 @@ import {
1819
type ServerFormData,
1920
} from "@/shared/types";
2021
import type { ChatboxMode, ChatboxSettings } from "@/hooks/useChatboxes";
22+
import {
23+
chatboxAccessPresetFromSettings,
24+
settingsFromChatboxAccessPreset,
25+
type ChatboxAccessPreset,
26+
} from "@/lib/chatbox-access-presets";
2127
import { useChatboxMutations } from "@/hooks/useChatboxes";
2228
import { useServerMutations } from "@/hooks/useWorkspaces";
2329
import { AddServerModal } from "@/components/connection/AddServerModal";
@@ -107,6 +113,7 @@ function isInsecureUrl(url: string | undefined): boolean {
107113
interface ChatboxEditorProps {
108114
chatbox?: ChatboxSettings | null;
109115
workspaceId: string;
116+
workspaceName?: string | null;
110117
workspaceServers: WorkspaceServerOption[];
111118
onBack: () => void;
112119
onSaved?: (chatbox: ChatboxSettings) => void;
@@ -130,6 +137,7 @@ function createPlaygroundId(): string {
130137
export function ChatboxEditor({
131138
chatbox,
132139
workspaceId,
140+
workspaceName,
133141
workspaceServers,
134142
onBack,
135143
onSaved,
@@ -299,6 +307,18 @@ export function ChatboxEditor({
299307
[workspaceServers],
300308
);
301309

310+
const workspaceAccessLabel = workspaceName?.trim() || "Workspace";
311+
const accessPreset = useMemo(
312+
() => chatboxAccessPresetFromSettings(mode, allowGuestAccess),
313+
[mode, allowGuestAccess],
314+
);
315+
316+
const applyCreateAccessPreset = (preset: ChatboxAccessPreset) => {
317+
const next = settingsFromChatboxAccessPreset(preset);
318+
setMode(next.mode);
319+
setAllowGuestAccess(next.allowGuestAccess);
320+
};
321+
302322
const previewToken = chatbox?.link?.token?.trim() || null;
303323
const canPreview = Boolean(previewToken);
304324
const previewName = name.trim() || chatbox?.name || "Chatbox Preview";
@@ -489,23 +509,6 @@ export function ChatboxEditor({
489509
chatbox,
490510
]);
491511

492-
const handleServerOptionalChange = (serverId: string, optional: boolean) => {
493-
const requiredIds = selectedServerIds.filter(
494-
(id) => !optionalServerIds.includes(id),
495-
);
496-
if (optional && requiredIds.length === 1 && requiredIds[0] === serverId) {
497-
toast.error(
498-
"Add another required server or mark another as required first.",
499-
);
500-
return;
501-
}
502-
setOptionalServerIds((current) =>
503-
optional
504-
? [...new Set([...current, serverId])]
505-
: current.filter((id) => id !== serverId),
506-
);
507-
};
508-
509512
const handleToggleServer = (serverId: string, checked: boolean) => {
510513
setSelectedServerIds((current) => {
511514
if (checked) {
@@ -901,9 +904,7 @@ export function ChatboxEditor({
901904
<ServerSelectionEditor
902905
workspaceServers={workspaceServers as RemoteServer[]}
903906
selectedServerIds={selectedServerIds}
904-
optionalServerIds={optionalServerIds}
905907
onToggleSelection={handleToggleServer}
906-
onOptionalChange={handleServerOptionalChange}
907908
onOpenAdd={() => setIsAddServerOpen(true)}
908909
/>
909910
</div>
@@ -976,18 +977,6 @@ export function ChatboxEditor({
976977
onCheckedChange={setRequireToolApproval}
977978
/>
978979
</div>
979-
<div className="flex items-center justify-between gap-3 rounded-md px-1 py-1.5">
980-
<div>
981-
<p className="text-sm">Allow guest access</p>
982-
<p className="text-[10px] text-muted-foreground">
983-
Unauthenticated visitors can use the chatbox link.
984-
</p>
985-
</div>
986-
<Switch
987-
checked={allowGuestAccess}
988-
onCheckedChange={setAllowGuestAccess}
989-
/>
990-
</div>
991980
</div>
992981
</div>
993982
</CollapsibleContent>
@@ -1001,7 +990,11 @@ export function ChatboxEditor({
1001990
<Label className="text-xs font-medium text-muted-foreground">
1002991
Sharing
1003992
</Label>
1004-
<ChatboxShareSection chatbox={chatbox} onUpdated={onSaved} />
993+
<ChatboxShareSection
994+
chatbox={chatbox}
995+
workspaceName={workspaceName}
996+
onUpdated={onSaved}
997+
/>
1005998
</div>
1006999
)}
10071000

@@ -1012,7 +1005,9 @@ export function ChatboxEditor({
10121005
<p className="text-sm font-medium">General access</p>
10131006
<div className="mt-2 flex items-start gap-3">
10141007
<div className="flex size-9 shrink-0 items-center justify-center rounded-full bg-muted">
1015-
{mode === "any_signed_in_with_link" ? (
1008+
{accessPreset === "workspace" ? (
1009+
<Users className="size-4 text-muted-foreground" />
1010+
) : accessPreset === "link_guests" ? (
10161011
<Globe className="size-4 text-muted-foreground" />
10171012
) : (
10181013
<Lock className="size-4 text-muted-foreground" />
@@ -1025,39 +1020,70 @@ export function ChatboxEditor({
10251020
type="button"
10261021
className="flex items-center gap-1 rounded-md px-1 py-0.5 text-sm font-medium hover:bg-muted/50 transition-colors"
10271022
>
1028-
{mode === "any_signed_in_with_link"
1029-
? "Anyone with the link"
1030-
: "Invited users only"}
1023+
{accessPreset === "workspace"
1024+
? workspaceAccessLabel
1025+
: accessPreset === "link_guests"
1026+
? "Anyone with the link (guests included)"
1027+
: "Invited users only"}
10311028
<ChevronDown className="size-3.5 text-muted-foreground" />
10321029
</button>
10331030
</PopoverTrigger>
1034-
<PopoverContent className="w-56 p-1" align="start">
1031+
<PopoverContent className="w-72 p-1" align="start">
10351032
<button
10361033
type="button"
1037-
className="flex w-full items-center justify-between rounded-md px-2 py-1.5 text-sm hover:bg-muted/50"
1038-
onClick={() => setMode("any_signed_in_with_link")}
1034+
className="flex w-full flex-col items-start gap-0.5 rounded-md px-2 py-2 text-left text-sm hover:bg-muted/50"
1035+
onClick={() => applyCreateAccessPreset("workspace")}
10391036
>
1040-
<span>Anyone with the link</span>
1041-
{mode === "any_signed_in_with_link" && (
1042-
<Check className="size-3.5 text-muted-foreground" />
1043-
)}
1037+
<span className="flex w-full items-center justify-between gap-2 font-medium">
1038+
{workspaceAccessLabel}
1039+
{accessPreset === "workspace" ? (
1040+
<Check className="size-3.5 shrink-0 text-muted-foreground" />
1041+
) : null}
1042+
</span>
1043+
<span className="text-xs text-muted-foreground">
1044+
Signed-in workspace members can use the link. Guests
1045+
cannot.
1046+
</span>
10441047
</button>
10451048
<button
10461049
type="button"
1047-
className="flex w-full items-center justify-between rounded-md px-2 py-1.5 text-sm hover:bg-muted/50"
1048-
onClick={() => setMode("invited_only")}
1050+
className="flex w-full flex-col items-start gap-0.5 rounded-md px-2 py-2 text-left text-sm hover:bg-muted/50"
1051+
onClick={() => applyCreateAccessPreset("invited_only")}
10491052
>
1050-
<span>Invited users only</span>
1051-
{mode === "invited_only" && (
1052-
<Check className="size-3.5 text-muted-foreground" />
1053-
)}
1053+
<span className="flex w-full items-center justify-between gap-2 font-medium">
1054+
Invited users only
1055+
{accessPreset === "invited_only" ? (
1056+
<Check className="size-3.5 shrink-0 text-muted-foreground" />
1057+
) : null}
1058+
</span>
1059+
<span className="text-xs text-muted-foreground">
1060+
Only people you invite by email can open this chatbox.
1061+
</span>
1062+
</button>
1063+
<button
1064+
type="button"
1065+
className="flex w-full flex-col items-start gap-0.5 rounded-md px-2 py-2 text-left text-sm hover:bg-muted/50"
1066+
onClick={() => applyCreateAccessPreset("link_guests")}
1067+
>
1068+
<span className="flex w-full items-center justify-between gap-2 font-medium">
1069+
Anyone with the link (guests included)
1070+
{accessPreset === "link_guests" ? (
1071+
<Check className="size-3.5 shrink-0 text-muted-foreground" />
1072+
) : null}
1073+
</span>
1074+
<span className="text-xs text-muted-foreground">
1075+
Anyone with the link, including guests without an
1076+
account.
1077+
</span>
10541078
</button>
10551079
</PopoverContent>
10561080
</Popover>
10571081
<p className="mt-0.5 px-1 text-xs text-muted-foreground">
1058-
{mode === "any_signed_in_with_link"
1059-
? "Any signed-in user with the link can open this chatbox."
1060-
: "Only people you've invited can access this chatbox."}
1082+
{accessPreset === "workspace"
1083+
? "Signed-in members of this workspace can open the chatbox with the link."
1084+
: accessPreset === "link_guests"
1085+
? "Anyone with the link can open this chatbox, including guests."
1086+
: "Only people you invite by email can open this chatbox."}
10611087
</p>
10621088
</div>
10631089
</div>

0 commit comments

Comments
 (0)