@agent-native/core 0.137.5 → 0.137.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +95 -17
- package/corpus/templates/clips/app/components/library/space-card.tsx +136 -66
- package/corpus/templates/clips/app/components/library/space-dialogs.tsx +144 -0
- package/corpus/templates/clips/app/hooks/use-library.ts +2 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +13 -0
- package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +6 -2
- package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +106 -87
- package/corpus/templates/clips/desktop/src/styles.css +19 -1
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +7 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +103 -49
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +4 -0
- package/corpus/templates/design/app/components/design/multi-screen/paint-suppression.ts +85 -0
- package/corpus/templates/design/changelog/2026-08-03-zoom-screen-blink.md +6 -0
- package/dist/agent/run-store.js +18 -0
- package/dist/client/AssistantChat.js +1 -0
- package/dist/client/chat/message-components.d.ts +10 -1
- package/dist/client/chat/message-components.js +13 -4
- package/dist/client/chat/runtime.js +20 -9
- package/dist/client/chat/tool-call-display.d.ts +7 -0
- package/dist/client/chat/tool-call-display.js +9 -1
- package/dist/client/sse-event-processor.d.ts +2 -0
- package/dist/client/sse-event-processor.js +39 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
|
@@ -32,9 +32,11 @@ import {
|
|
|
32
32
|
IconShare,
|
|
33
33
|
IconSettings,
|
|
34
34
|
IconSearch,
|
|
35
|
+
IconDots,
|
|
36
|
+
IconEdit,
|
|
35
37
|
} from "@tabler/icons-react";
|
|
36
38
|
import { ReactNode, useEffect, useMemo, useState } from "react";
|
|
37
|
-
import { NavLink, useLocation, useParams } from "react-router";
|
|
39
|
+
import { NavLink, useLocation, useNavigate, useParams } from "react-router";
|
|
38
40
|
import { toast } from "sonner";
|
|
39
41
|
|
|
40
42
|
import {
|
|
@@ -52,6 +54,12 @@ import {
|
|
|
52
54
|
AlertDialogTitle,
|
|
53
55
|
} from "@/components/ui/alert-dialog";
|
|
54
56
|
import { Button } from "@/components/ui/button";
|
|
57
|
+
import {
|
|
58
|
+
DropdownMenu,
|
|
59
|
+
DropdownMenuContent,
|
|
60
|
+
DropdownMenuItem,
|
|
61
|
+
DropdownMenuTrigger,
|
|
62
|
+
} from "@/components/ui/dropdown-menu";
|
|
55
63
|
import {
|
|
56
64
|
Tooltip,
|
|
57
65
|
TooltipContent,
|
|
@@ -73,6 +81,7 @@ import { CreateSpaceDialog } from "./create-space-dialog";
|
|
|
73
81
|
import { FolderTree, type FolderNode } from "./folder-tree";
|
|
74
82
|
import { PageHeaderSlotProvider } from "./page-header";
|
|
75
83
|
import { SearchBar } from "./search-bar";
|
|
84
|
+
import { SpaceDialogs } from "./space-dialogs";
|
|
76
85
|
|
|
77
86
|
interface LibraryLayoutProps {
|
|
78
87
|
children: ReactNode;
|
|
@@ -98,6 +107,7 @@ function ClipsAgentToggleButton() {
|
|
|
98
107
|
|
|
99
108
|
export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
100
109
|
const location = useLocation();
|
|
110
|
+
const navigate = useNavigate();
|
|
101
111
|
const t = useT();
|
|
102
112
|
// Bind chat to the currently-open recording (`/r/:id`). Library, spaces,
|
|
103
113
|
// meetings, dictate, and settings stay unscoped — those are list-y views
|
|
@@ -123,9 +133,12 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
123
133
|
const currentOrganizationId =
|
|
124
134
|
organizations?.currentId ?? organizations?.organizations?.[0]?.id;
|
|
125
135
|
|
|
126
|
-
const { data: spaces } = useSpaces(
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
const { data: spaces, refetch: refetchSpaces } = useSpaces(
|
|
137
|
+
currentOrganizationId,
|
|
138
|
+
{
|
|
139
|
+
enabled: hasActiveOrg && Boolean(currentOrganizationId),
|
|
140
|
+
},
|
|
141
|
+
);
|
|
129
142
|
const { data: libFolders } = useFolders(
|
|
130
143
|
{
|
|
131
144
|
organizationId: currentOrganizationId,
|
|
@@ -240,6 +253,10 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
240
253
|
const [newFolderOpen, setNewFolderOpen] = useState(false);
|
|
241
254
|
const [newFolderName, setNewFolderName] = useState("");
|
|
242
255
|
const [newSpaceOpen, setNewSpaceOpen] = useState(false);
|
|
256
|
+
const [deleteSpaceId, setDeleteSpaceId] = useState<string | null>(null);
|
|
257
|
+
const [deleteSpaceName, setDeleteSpaceName] = useState("");
|
|
258
|
+
const [renameSpaceId, setRenameSpaceId] = useState<string | null>(null);
|
|
259
|
+
const [renameSpaceValue, setRenameSpaceValue] = useState("");
|
|
243
260
|
const createFolder = useCreateFolder();
|
|
244
261
|
|
|
245
262
|
const navItems: {
|
|
@@ -540,26 +557,70 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
540
557
|
const active = spaceId === s.id;
|
|
541
558
|
return (
|
|
542
559
|
<li key={s.id}>
|
|
543
|
-
<
|
|
544
|
-
to={`/spaces/${s.id}`}
|
|
560
|
+
<div
|
|
545
561
|
className={cn(
|
|
546
|
-
"flex items-center gap-2 rounded px-2 py-1 text-xs",
|
|
562
|
+
"group flex items-center gap-2 rounded px-2 py-1 text-xs",
|
|
547
563
|
active
|
|
548
564
|
? "bg-primary/10 text-primary"
|
|
549
565
|
: "text-foreground hover:bg-accent/60",
|
|
550
566
|
)}
|
|
551
567
|
>
|
|
552
|
-
<
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
background: s.color ?? "hsl(var(--primary))",
|
|
556
|
-
color: "white",
|
|
557
|
-
}}
|
|
568
|
+
<NavLink
|
|
569
|
+
to={`/spaces/${s.id}`}
|
|
570
|
+
className="flex min-w-0 flex-1 items-center gap-2"
|
|
558
571
|
>
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
572
|
+
<div
|
|
573
|
+
className="flex h-4 w-4 items-center justify-center rounded text-[10px] shrink-0"
|
|
574
|
+
style={{
|
|
575
|
+
background: s.color ?? "hsl(var(--primary))",
|
|
576
|
+
color: "white",
|
|
577
|
+
}}
|
|
578
|
+
>
|
|
579
|
+
{s.iconEmoji ??
|
|
580
|
+
s.name.slice(0, 1).toUpperCase()}
|
|
581
|
+
</div>
|
|
582
|
+
<span className="truncate">{s.name}</span>
|
|
583
|
+
</NavLink>
|
|
584
|
+
{canManageOrg && (
|
|
585
|
+
<DropdownMenu>
|
|
586
|
+
<DropdownMenuTrigger asChild>
|
|
587
|
+
<button
|
|
588
|
+
type="button"
|
|
589
|
+
aria-label={`${s.name}: ${t("root.commandActions")}`}
|
|
590
|
+
title={`${s.name}: ${t("root.commandActions")}`}
|
|
591
|
+
className="rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100"
|
|
592
|
+
>
|
|
593
|
+
<IconDots className="h-3.5 w-3.5" />
|
|
594
|
+
</button>
|
|
595
|
+
</DropdownMenuTrigger>
|
|
596
|
+
<DropdownMenuContent align="start" side="right">
|
|
597
|
+
<DropdownMenuItem
|
|
598
|
+
onSelect={() => {
|
|
599
|
+
setTimeout(() => {
|
|
600
|
+
setRenameSpaceValue(s.name);
|
|
601
|
+
setRenameSpaceId(s.id);
|
|
602
|
+
}, 0);
|
|
603
|
+
}}
|
|
604
|
+
>
|
|
605
|
+
<IconEdit className="h-3.5 w-3.5 me-2" />
|
|
606
|
+
{t("spaceDialog.renameSpace")}
|
|
607
|
+
</DropdownMenuItem>
|
|
608
|
+
<DropdownMenuItem
|
|
609
|
+
onSelect={() => {
|
|
610
|
+
setTimeout(() => {
|
|
611
|
+
setDeleteSpaceId(s.id);
|
|
612
|
+
setDeleteSpaceName(s.name);
|
|
613
|
+
}, 0);
|
|
614
|
+
}}
|
|
615
|
+
className="text-destructive"
|
|
616
|
+
>
|
|
617
|
+
<IconTrash className="h-3.5 w-3.5 me-2" />
|
|
618
|
+
{t("spaceDialog.deleteSpace")}
|
|
619
|
+
</DropdownMenuItem>
|
|
620
|
+
</DropdownMenuContent>
|
|
621
|
+
</DropdownMenu>
|
|
622
|
+
)}
|
|
623
|
+
</div>
|
|
563
624
|
</li>
|
|
564
625
|
);
|
|
565
626
|
})}
|
|
@@ -781,6 +842,23 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
781
842
|
onOpenChange={setNewSpaceOpen}
|
|
782
843
|
organizationId={currentOrganizationId}
|
|
783
844
|
/>
|
|
845
|
+
|
|
846
|
+
<SpaceDialogs
|
|
847
|
+
renameSpaceId={renameSpaceId}
|
|
848
|
+
renameSpaceName=""
|
|
849
|
+
setRenameSpaceId={setRenameSpaceId}
|
|
850
|
+
renameValue={renameSpaceValue}
|
|
851
|
+
setRenameValue={setRenameSpaceValue}
|
|
852
|
+
deleteSpaceId={deleteSpaceId}
|
|
853
|
+
deleteSpaceName={deleteSpaceName}
|
|
854
|
+
setDeleteSpaceId={setDeleteSpaceId}
|
|
855
|
+
onMutationSuccess={(deletedSpaceId) => {
|
|
856
|
+
if (deletedSpaceId && spaceId === deletedSpaceId) {
|
|
857
|
+
navigate("/spaces");
|
|
858
|
+
}
|
|
859
|
+
refetchSpaces?.();
|
|
860
|
+
}}
|
|
861
|
+
/>
|
|
784
862
|
</div>
|
|
785
863
|
);
|
|
786
864
|
}
|
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { useOrgRole } from "@agent-native/core/client/org";
|
|
3
|
+
import {
|
|
4
|
+
IconUsersGroup,
|
|
5
|
+
IconVideo,
|
|
6
|
+
IconTrash,
|
|
7
|
+
IconEdit,
|
|
8
|
+
} from "@tabler/icons-react";
|
|
9
|
+
import { useState } from "react";
|
|
2
10
|
import { Link } from "react-router";
|
|
3
11
|
|
|
12
|
+
import {
|
|
13
|
+
ContextMenu,
|
|
14
|
+
ContextMenuContent,
|
|
15
|
+
ContextMenuItem,
|
|
16
|
+
ContextMenuTrigger,
|
|
17
|
+
} from "@/components/ui/context-menu";
|
|
4
18
|
import { cn } from "@/lib/utils";
|
|
5
19
|
|
|
20
|
+
import { SpaceDialogs } from "./space-dialogs";
|
|
21
|
+
|
|
6
22
|
export interface SpaceCardData {
|
|
7
23
|
id: string;
|
|
8
24
|
name: string;
|
|
@@ -16,80 +32,134 @@ export interface SpaceCardData {
|
|
|
16
32
|
interface SpaceCardProps {
|
|
17
33
|
space: SpaceCardData;
|
|
18
34
|
className?: string;
|
|
35
|
+
onMutationSuccess?: () => void;
|
|
19
36
|
}
|
|
20
37
|
|
|
21
|
-
export function SpaceCard({
|
|
38
|
+
export function SpaceCard({
|
|
39
|
+
space,
|
|
40
|
+
className,
|
|
41
|
+
onMutationSuccess,
|
|
42
|
+
}: SpaceCardProps) {
|
|
43
|
+
const t = useT();
|
|
44
|
+
const { canManageOrg } = useOrgRole();
|
|
22
45
|
const color = space.color || "hsl(var(--primary))";
|
|
23
46
|
const members = space.memberEmails ?? [];
|
|
24
47
|
const initial = (space.name.trim().slice(0, 1) || "S").toUpperCase();
|
|
48
|
+
const [renameSpaceId, setRenameSpaceId] = useState<string | null>(null);
|
|
49
|
+
const [renameValue, setRenameValue] = useState("");
|
|
50
|
+
const [deleteSpaceId, setDeleteSpaceId] = useState<string | null>(null);
|
|
25
51
|
|
|
26
52
|
return (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{members.slice(0, 5).map((email) => {
|
|
72
|
-
const initials = (email.split("@")[0] || "?")
|
|
73
|
-
.slice(0, 2)
|
|
74
|
-
.toUpperCase();
|
|
75
|
-
return (
|
|
76
|
-
<div
|
|
77
|
-
key={email}
|
|
78
|
-
title={email}
|
|
79
|
-
className="flex h-5 w-5 items-center justify-center rounded-full border border-background bg-primary/15 text-[9px] font-medium text-primary"
|
|
80
|
-
>
|
|
81
|
-
{initials}
|
|
53
|
+
<>
|
|
54
|
+
<ContextMenu>
|
|
55
|
+
<ContextMenuTrigger asChild>
|
|
56
|
+
<Link
|
|
57
|
+
to={`/spaces/${space.id}`}
|
|
58
|
+
className={cn(
|
|
59
|
+
"group flex flex-col overflow-hidden rounded-lg border border-border bg-card text-start",
|
|
60
|
+
"hover:border-primary/40",
|
|
61
|
+
"shadow-[0_1px_2px_rgba(15,23,42,0.04)] hover:shadow-md",
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
<div
|
|
66
|
+
className="relative flex h-24 items-center justify-center"
|
|
67
|
+
style={{
|
|
68
|
+
background: `linear-gradient(135deg, ${color} 0%, ${color}dd 100%)`,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
{space.iconEmoji ? (
|
|
72
|
+
<span className="text-3xl">{space.iconEmoji}</span>
|
|
73
|
+
) : (
|
|
74
|
+
<span className="text-3xl font-semibold text-white">
|
|
75
|
+
{initial}
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
<div className="flex flex-1 flex-col p-3">
|
|
80
|
+
<h3 className="text-sm font-semibold text-foreground truncate">
|
|
81
|
+
{space.name}
|
|
82
|
+
</h3>
|
|
83
|
+
<div className="mt-2 flex items-center justify-between text-[11px] text-muted-foreground">
|
|
84
|
+
<div className="flex items-center gap-1">
|
|
85
|
+
<IconUsersGroup className="h-3.5 w-3.5" />
|
|
86
|
+
<span>
|
|
87
|
+
{space.memberCount ?? members.length} member
|
|
88
|
+
{(space.memberCount ?? members.length) === 1 ? "" : "s"}
|
|
89
|
+
</span>
|
|
90
|
+
</div>
|
|
91
|
+
<div className="flex items-center gap-1">
|
|
92
|
+
<IconVideo className="h-3.5 w-3.5" />
|
|
93
|
+
<span>
|
|
94
|
+
{space.recordingCount ?? 0} recording
|
|
95
|
+
{(space.recordingCount ?? 0) === 1 ? "" : "s"}
|
|
96
|
+
</span>
|
|
82
97
|
</div>
|
|
83
|
-
);
|
|
84
|
-
})}
|
|
85
|
-
{members.length > 5 && (
|
|
86
|
-
<div className="flex h-5 w-5 items-center justify-center rounded-full border border-background bg-muted text-[9px] text-muted-foreground">
|
|
87
|
-
+{members.length - 5}
|
|
88
98
|
</div>
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
|
|
100
|
+
{members.length > 0 && (
|
|
101
|
+
<div className="mt-2 flex -space-x-1">
|
|
102
|
+
{members.slice(0, 5).map((email) => {
|
|
103
|
+
const initials = (email.split("@")[0] || "?")
|
|
104
|
+
.slice(0, 2)
|
|
105
|
+
.toUpperCase();
|
|
106
|
+
return (
|
|
107
|
+
<div
|
|
108
|
+
key={email}
|
|
109
|
+
title={email}
|
|
110
|
+
className="flex h-5 w-5 items-center justify-center rounded-full border border-background bg-primary/15 text-[9px] font-medium text-primary"
|
|
111
|
+
>
|
|
112
|
+
{initials}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
})}
|
|
116
|
+
{members.length > 5 && (
|
|
117
|
+
<div className="flex h-5 w-5 items-center justify-center rounded-full border border-background bg-muted text-[9px] text-muted-foreground">
|
|
118
|
+
+{members.length - 5}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</Link>
|
|
125
|
+
</ContextMenuTrigger>
|
|
126
|
+
{canManageOrg && (
|
|
127
|
+
<ContextMenuContent>
|
|
128
|
+
<ContextMenuItem
|
|
129
|
+
onSelect={() => {
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
setRenameValue(space.name);
|
|
132
|
+
setRenameSpaceId(space.id);
|
|
133
|
+
}, 0);
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
<IconEdit className="h-4 w-4 mr-2" />
|
|
137
|
+
{t("spaceDialog.renameSpace")}
|
|
138
|
+
</ContextMenuItem>
|
|
139
|
+
<ContextMenuItem
|
|
140
|
+
onSelect={() => {
|
|
141
|
+
setTimeout(() => setDeleteSpaceId(space.id), 0);
|
|
142
|
+
}}
|
|
143
|
+
className="text-destructive focus:text-destructive"
|
|
144
|
+
>
|
|
145
|
+
<IconTrash className="h-4 w-4 mr-2" />
|
|
146
|
+
{t("spaceDialog.deleteSpace")}
|
|
147
|
+
</ContextMenuItem>
|
|
148
|
+
</ContextMenuContent>
|
|
91
149
|
)}
|
|
92
|
-
</
|
|
93
|
-
|
|
150
|
+
</ContextMenu>
|
|
151
|
+
|
|
152
|
+
<SpaceDialogs
|
|
153
|
+
renameSpaceId={renameSpaceId}
|
|
154
|
+
renameSpaceName={space.name}
|
|
155
|
+
setRenameSpaceId={setRenameSpaceId}
|
|
156
|
+
renameValue={renameValue}
|
|
157
|
+
setRenameValue={setRenameValue}
|
|
158
|
+
deleteSpaceId={deleteSpaceId}
|
|
159
|
+
deleteSpaceName={space.name}
|
|
160
|
+
setDeleteSpaceId={setDeleteSpaceId}
|
|
161
|
+
onMutationSuccess={onMutationSuccess}
|
|
162
|
+
/>
|
|
163
|
+
</>
|
|
94
164
|
);
|
|
95
165
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { useActionMutation } from "@agent-native/core/client/hooks";
|
|
2
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
AlertDialog,
|
|
8
|
+
AlertDialogAction,
|
|
9
|
+
AlertDialogCancel,
|
|
10
|
+
AlertDialogContent,
|
|
11
|
+
AlertDialogDescription,
|
|
12
|
+
AlertDialogTitle,
|
|
13
|
+
} from "@/components/ui/alert-dialog";
|
|
14
|
+
|
|
15
|
+
interface SpaceDialogsProps {
|
|
16
|
+
renameSpaceId: string | null;
|
|
17
|
+
renameSpaceName: string;
|
|
18
|
+
setRenameSpaceId: (id: string | null) => void;
|
|
19
|
+
renameValue: string;
|
|
20
|
+
setRenameValue: (value: string) => void;
|
|
21
|
+
deleteSpaceId: string | null;
|
|
22
|
+
deleteSpaceName: string;
|
|
23
|
+
setDeleteSpaceId: (id: string | null) => void;
|
|
24
|
+
onMutationSuccess?: (deletedSpaceId?: string) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function SpaceDialogs({
|
|
28
|
+
renameSpaceId,
|
|
29
|
+
renameSpaceName,
|
|
30
|
+
setRenameSpaceId,
|
|
31
|
+
renameValue,
|
|
32
|
+
setRenameValue,
|
|
33
|
+
deleteSpaceId,
|
|
34
|
+
deleteSpaceName,
|
|
35
|
+
setDeleteSpaceId,
|
|
36
|
+
onMutationSuccess,
|
|
37
|
+
}: SpaceDialogsProps) {
|
|
38
|
+
const t = useT();
|
|
39
|
+
const renameSpace = useActionMutation("rename-space");
|
|
40
|
+
const deleteSpace = useActionMutation("delete-space");
|
|
41
|
+
|
|
42
|
+
const handleRename = async () => {
|
|
43
|
+
if (!renameSpaceId) return;
|
|
44
|
+
try {
|
|
45
|
+
await renameSpace.mutateAsync({
|
|
46
|
+
id: renameSpaceId,
|
|
47
|
+
name: renameValue.trim(),
|
|
48
|
+
});
|
|
49
|
+
toast.success(t("spaceDialog.renamed"));
|
|
50
|
+
setRenameSpaceId(null);
|
|
51
|
+
setRenameValue("");
|
|
52
|
+
onMutationSuccess?.();
|
|
53
|
+
} catch (err) {
|
|
54
|
+
toast.error(
|
|
55
|
+
err instanceof Error ? err.message : t("spaceDialog.renameFailed"),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleDelete = async () => {
|
|
61
|
+
if (!deleteSpaceId) return;
|
|
62
|
+
try {
|
|
63
|
+
await deleteSpace.mutateAsync({ id: deleteSpaceId });
|
|
64
|
+
toast.success(t("spaceDialog.deleted", { name: deleteSpaceName }));
|
|
65
|
+
setDeleteSpaceId(null);
|
|
66
|
+
onMutationSuccess?.(deleteSpaceId);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
toast.error(
|
|
69
|
+
err instanceof Error ? err.message : t("spaceDialog.deleteFailed"),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
{/* Rename space dialog */}
|
|
77
|
+
<AlertDialog
|
|
78
|
+
open={renameSpaceId !== null}
|
|
79
|
+
onOpenChange={(open) => {
|
|
80
|
+
if (!open) {
|
|
81
|
+
setRenameSpaceId(null);
|
|
82
|
+
setRenameValue("");
|
|
83
|
+
}
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<AlertDialogContent>
|
|
87
|
+
<AlertDialogTitle>{t("spaceDialog.renameSpace")}</AlertDialogTitle>
|
|
88
|
+
<input
|
|
89
|
+
autoFocus
|
|
90
|
+
value={renameValue}
|
|
91
|
+
onChange={(e) => setRenameValue(e.target.value)}
|
|
92
|
+
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary/30"
|
|
93
|
+
/>
|
|
94
|
+
<div className="flex justify-end gap-2">
|
|
95
|
+
<AlertDialogCancel>{t("common.cancel")}</AlertDialogCancel>
|
|
96
|
+
<AlertDialogAction
|
|
97
|
+
onClick={(event) => {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
void handleRename();
|
|
100
|
+
}}
|
|
101
|
+
disabled={renameSpace.isPending || !renameValue.trim()}
|
|
102
|
+
>
|
|
103
|
+
{renameSpace.isPending
|
|
104
|
+
? t("spaceDialog.renaming")
|
|
105
|
+
: t("spaceDialog.renameSpace")}
|
|
106
|
+
</AlertDialogAction>
|
|
107
|
+
</div>
|
|
108
|
+
</AlertDialogContent>
|
|
109
|
+
</AlertDialog>
|
|
110
|
+
|
|
111
|
+
{/* Delete space dialog */}
|
|
112
|
+
<AlertDialog
|
|
113
|
+
open={deleteSpaceId !== null}
|
|
114
|
+
onOpenChange={(open) => {
|
|
115
|
+
if (!open) setDeleteSpaceId(null);
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
<AlertDialogContent>
|
|
119
|
+
<AlertDialogTitle>
|
|
120
|
+
{t("spaceDialog.deleteTitle", { name: deleteSpaceName })}
|
|
121
|
+
</AlertDialogTitle>
|
|
122
|
+
<AlertDialogDescription>
|
|
123
|
+
{t("spaceDialog.deleteDescription")}
|
|
124
|
+
</AlertDialogDescription>
|
|
125
|
+
<div className="flex justify-end gap-2">
|
|
126
|
+
<AlertDialogCancel>{t("common.cancel")}</AlertDialogCancel>
|
|
127
|
+
<AlertDialogAction
|
|
128
|
+
onClick={(event) => {
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
void handleDelete();
|
|
131
|
+
}}
|
|
132
|
+
disabled={deleteSpace.isPending}
|
|
133
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
134
|
+
>
|
|
135
|
+
{deleteSpace.isPending
|
|
136
|
+
? t("spaceDialog.deleting")
|
|
137
|
+
: t("spaceDialog.deleteSpace")}
|
|
138
|
+
</AlertDialogAction>
|
|
139
|
+
</div>
|
|
140
|
+
</AlertDialogContent>
|
|
141
|
+
</AlertDialog>
|
|
142
|
+
</>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
@@ -240,11 +240,11 @@ export function useSpaces(
|
|
|
240
240
|
organizationId?: string,
|
|
241
241
|
options: { enabled?: boolean } = {},
|
|
242
242
|
) {
|
|
243
|
-
const { data, isLoading } = useOrganizationState(organizationId, {
|
|
243
|
+
const { data, isLoading, refetch } = useOrganizationState(organizationId, {
|
|
244
244
|
enabled: options.enabled ?? Boolean(organizationId),
|
|
245
245
|
});
|
|
246
246
|
const spaces = Array.isArray(data?.spaces) ? (data.spaces as any[]) : [];
|
|
247
|
-
return { data: { spaces }, isLoading };
|
|
247
|
+
return { data: { spaces }, isLoading, refetch };
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
export function useOrganizations(options: { enabled?: boolean } = {}) {
|
|
@@ -1101,6 +1101,19 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
1101
1101
|
spaceCreated: "Space created",
|
|
1102
1102
|
createFailed: "Could not create space",
|
|
1103
1103
|
},
|
|
1104
|
+
spaceDialog: {
|
|
1105
|
+
deleteSpace: "Delete space",
|
|
1106
|
+
renameSpace: "Rename space",
|
|
1107
|
+
deleteTitle: 'Delete "{{name}}"?',
|
|
1108
|
+
deleteDescription:
|
|
1109
|
+
"This will delete the space and remove it from all recordings. This action cannot be undone.",
|
|
1110
|
+
renamed: "Space renamed",
|
|
1111
|
+
deleted: 'Deleted "{{name}}"',
|
|
1112
|
+
renameFailed: "Failed to rename space",
|
|
1113
|
+
deleteFailed: "Failed to delete space",
|
|
1114
|
+
renaming: "Renaming...",
|
|
1115
|
+
deleting: "Deleting...",
|
|
1116
|
+
},
|
|
1104
1117
|
signInPrompt: {
|
|
1105
1118
|
title: "Sign in to {{intent}}",
|
|
1106
1119
|
description:
|
|
@@ -34,7 +34,7 @@ export default function SpacesIndexRoute() {
|
|
|
34
34
|
const { data: organizations } = useOrganizations();
|
|
35
35
|
const currentOrganizationId =
|
|
36
36
|
organizations?.currentId ?? organizations?.organizations?.[0]?.id;
|
|
37
|
-
const { data, isLoading } = useSpaces(currentOrganizationId);
|
|
37
|
+
const { data, isLoading, refetch } = useSpaces(currentOrganizationId);
|
|
38
38
|
|
|
39
39
|
const spaces: SpaceCardData[] = (data?.spaces ?? []).map((s: any) => ({
|
|
40
40
|
id: s.id,
|
|
@@ -80,7 +80,11 @@ export default function SpacesIndexRoute() {
|
|
|
80
80
|
) : (
|
|
81
81
|
<div className="grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(240px,1fr))]">
|
|
82
82
|
{spaces.map((s) => (
|
|
83
|
-
<SpaceCard
|
|
83
|
+
<SpaceCard
|
|
84
|
+
key={s.id}
|
|
85
|
+
space={s}
|
|
86
|
+
onMutationSuccess={() => refetch?.()}
|
|
87
|
+
/>
|
|
84
88
|
))}
|
|
85
89
|
</div>
|
|
86
90
|
)}
|