fix(web): render the create-bot modal outside the side panel; tidy Shell leftovers (#131)

The centered Create new Bot modal was rendered inside the side panel's
condition, so the empty states that open it (nothing selected, or a
project without bots) only showed it while a panel happened to be open.
It is a portal to the document body, so it now renders after the panel.

- The project drag grip's accessible name goes through Lingui (German
  "Ziehen zum Umsortieren") instead of a hard-coded English label.
- Drop the project drag state that was set but never read, and imports
  Shell.tsx no longer uses.


Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
cynarAI 2026-10-04 16:43:50 +02:00 committed by GitHub
parent e928600c98
commit 68b1e63c46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 79 additions and 59 deletions

View File

@ -4,6 +4,7 @@
*/
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useLingui } from "@lingui/react/macro";
import { createContext, memo, useContext, useMemo } from "react";
interface SortableGroupProps {
@ -30,13 +31,14 @@ export const ProjectDragGrip = memo(function ProjectDragGrip({
className?: string;
children: React.ReactNode;
}) {
const { t } = useLingui();
const handle = useSortableGroupHandle();
return (
<button
type="button"
ref={handle?.setActivatorNodeRef}
className={className}
aria-label="Drag to reorder"
aria-label={t`Drag to reorder`}
{...handle?.attributes}
{...handle?.listeners}
>

View File

@ -2100,6 +2100,10 @@ msgstr "Skill-Entwurf"
msgid "Drafting the spec…"
msgstr "Entwerfe die Spezifikation…"
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr "Ziehen zum Umsortieren"
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "Bild ziehen, ablegen oder einfügen"

View File

@ -2100,6 +2100,10 @@ msgstr "Draft skill"
msgid "Drafting the spec…"
msgstr "Drafting the spec…"
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr "Drag to reorder"
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "Drag, drop, or paste an image"

View File

@ -2100,6 +2100,10 @@ msgstr "Habilidad en borrador"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "Arrastra, suelta o pega una imagen"

View File

@ -2100,6 +2100,10 @@ msgstr "ड्राफ़्ट स्किल"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "छवि खींचें, छोड़ें या पेस्ट करें"

View File

@ -2100,6 +2100,10 @@ msgstr "작업 기술 초안"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "이미지를 끌어다 놓거나 붙여넣으세요"

View File

@ -2100,6 +2100,10 @@ msgstr "Habilidade de rascunho"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "Arraste, solte ou cole uma imagem"

View File

@ -2100,6 +2100,10 @@ msgstr "Черновик навыка"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "Перетащите или вставьте изображение"

View File

@ -2100,6 +2100,10 @@ msgstr "Taslak beceri"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "Görüntüyü sürükleyip bırakın veya yapıştırın"

View File

@ -2100,6 +2100,10 @@ msgstr "技能草稿"
msgid "Drafting the spec…"
msgstr ""
#: src/components/SortableGroup.tsx
msgid "Drag to reorder"
msgstr ""
#: src/pages/shell/avatar-studio-popover.tsx
msgid "Drag, drop, or paste an image"
msgstr "拖放或粘贴图片"

View File

@ -1,10 +1,5 @@
import { closestCenter, DndContext, DragOverlay } from "@dnd-kit/core";
import {
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS as DndCSS } from "@dnd-kit/utilities";
import { closestCenter, DndContext } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { Trans, useLingui } from "@lingui/react/macro";
import type {
Bot,
@ -72,13 +67,10 @@ import {
ArrowLeft,
Calendar,
Globe,
LayoutGrid,
PanelRight,
Pencil,
Plus,
RefreshCw,
Search,
Settings,
Trash2,
X,
} from "lucide-react";
@ -1320,9 +1312,7 @@ export function ShellPage() {
mainConversationEntry,
projectSidebarGroups,
sensors,
setDraggedProjectId,
handleProjectDragEnd,
handleProjectDragStart,
} = useSidebarModel({
query,
spaces,
@ -2943,8 +2933,6 @@ export function ShellPage() {
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleProjectDragEnd}
onDragStart={handleProjectDragStart}
onDragCancel={() => setDraggedProjectId(null)}
>
<SortableContext
items={projectSidebarGroups.filter((g) => g.title).map((g) => g.spaceId)}
@ -3544,44 +3532,6 @@ export function ShellPage() {
onImport={(manifest) => importBot(manifest)}
/>
) : null}
{/* Centered Create Bot Modal */}
{showCreateBotModal &&
createPortal(
<div className="fixed inset-0 z-50 flex items-center justify-center">
<button
type="button"
className="absolute inset-0 bg-overlay"
aria-label={t`Close`}
onClick={() => setShowCreateBotModal(false)}
/>
<div className="relative z-10 max-h-[90vh] w-[480px] overflow-y-auto rounded-2xl border border-border bg-popover p-6 text-popover-foreground shadow-2xl">
<div className="mb-4 flex items-center justify-between">
<h2 className="text-lg font-semibold text-foreground">
<Trans>Create new Bot</Trans>
</h2>
<button
type="button"
onClick={() => setShowCreateBotModal(false)}
className="text-muted-foreground hover:text-foreground"
aria-label={t`Close`}
>
<X strokeWidth={1.8} className="size-icon-md" />
</button>
</div>
<CreateBotForm
onCancel={() => setShowCreateBotModal(false)}
onCreate={async (input) => {
await createBot(input);
}}
onImport={async (manifest) => {
await importBot(manifest);
setShowCreateBotModal(false);
}}
/>
</div>
</div>,
document.body,
)}
{panel === "settings" && active ? (
<BotSettings
key={active.id}
@ -3773,6 +3723,44 @@ export function ShellPage() {
</div>
) : null}
</aside>
{/* Centered Create Bot Modal */}
{showCreateBotModal &&
createPortal(
<div className="fixed inset-0 z-50 flex items-center justify-center">
<button
type="button"
className="absolute inset-0 bg-overlay"
aria-label={t`Close`}
onClick={() => setShowCreateBotModal(false)}
/>
<div className="relative z-10 max-h-[90vh] w-[480px] overflow-y-auto rounded-2xl border border-border bg-popover p-6 text-popover-foreground shadow-2xl">
<div className="mb-4 flex items-center justify-between">
<h2 className="text-lg font-semibold text-foreground">
<Trans>Create new Bot</Trans>
</h2>
<button
type="button"
onClick={() => setShowCreateBotModal(false)}
className="text-muted-foreground hover:text-foreground"
aria-label={t`Close`}
>
<X strokeWidth={1.8} className="size-icon-md" />
</button>
</div>
<CreateBotForm
onCancel={() => setShowCreateBotModal(false)}
onCreate={async (input) => {
await createBot(input);
}}
onImport={async (manifest) => {
await importBot(manifest);
setShowCreateBotModal(false);
}}
/>
</div>
</div>,
document.body,
)}
<Suspense fallback={null}>
{contextChat && botMenu ? (

View File

@ -32,7 +32,6 @@ export function useSidebarModel({
setProjectOrder(orderedIds);
writeProjectSidebarOrder(orderedIds);
}, []);
const [draggedProjectId, setDraggedProjectId] = useState<string | null>(null);
const sensors = useSensors(
useSensor(MouseSensor, { activationConstraint: { distance: 8 } }),
useSensor(TouchSensor, { activationConstraint: { distance: 8 } }),
@ -242,9 +241,6 @@ export function useSidebarModel({
},
[projectSidebarGroups, handleReorderProjects],
);
const handleProjectDragStart = useCallback((event: { active: { id: string | number } }) => {
setDraggedProjectId(String(event.active.id));
}, []);
return {
homeSpaceBots,
@ -252,8 +248,6 @@ export function useSidebarModel({
mainConversationEntry,
projectSidebarGroups,
sensors,
setDraggedProjectId,
handleProjectDragEnd,
handleProjectDragStart,
};
}