+
-
-
-
-
+
-
+
+
+
+
+ {activeWorkspace?.worktreePath && (
+
+ )}
{!isMac &&
}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx
new file mode 100644
index 000000000..3cce23d23
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx
@@ -0,0 +1,206 @@
+import { toast } from "@superset/ui/sonner";
+import { useCallback, useEffect, useRef } from "react";
+import { trpc } from "renderer/lib/trpc";
+import { useOpenConfigModal } from "renderer/stores/config-modal";
+import { useTabsStore } from "renderer/stores/tabs/store";
+import {
+ type PendingTerminalSetup,
+ useWorkspaceInitStore,
+} from "renderer/stores/workspace-init";
+
+/**
+ * Renderless component that handles terminal setup when workspaces become ready.
+ *
+ * This is mounted at the app root (MainScreen) so it survives dialog unmounts.
+ * When a workspace creation is initiated from a dialog (e.g., InitGitDialog,
+ * CloneRepoDialog), the dialog may close before initialization completes.
+ * This component ensures the terminal is still created when the workspace
+ * becomes ready.
+ *
+ * Also handles the case where pending setup data is lost (e.g., after retry
+ * or app restart) by fetching setup commands from the backend on demand.
+ */
+export function WorkspaceInitEffects() {
+ const initProgress = useWorkspaceInitStore((s) => s.initProgress);
+ const pendingTerminalSetups = useWorkspaceInitStore(
+ (s) => s.pendingTerminalSetups,
+ );
+ const removePendingTerminalSetup = useWorkspaceInitStore(
+ (s) => s.removePendingTerminalSetup,
+ );
+ const clearProgress = useWorkspaceInitStore((s) => s.clearProgress);
+
+ // Track which setups are currently being processed to prevent duplicate handling
+ const processingRef = useRef
>(new Set());
+
+ const addTab = useTabsStore((state) => state.addTab);
+ const setTabAutoTitle = useTabsStore((state) => state.setTabAutoTitle);
+ const createOrAttach = trpc.terminal.createOrAttach.useMutation();
+ const openConfigModal = useOpenConfigModal();
+ const dismissConfigToast = trpc.config.dismissConfigToast.useMutation();
+ const utils = trpc.useUtils();
+
+ // Helper to create terminal with setup commands
+ const handleTerminalSetup = useCallback(
+ (setup: PendingTerminalSetup, onComplete: () => void) => {
+ if (
+ Array.isArray(setup.initialCommands) &&
+ setup.initialCommands.length > 0
+ ) {
+ const { tabId, paneId } = addTab(setup.workspaceId);
+ setTabAutoTitle(tabId, "Workspace Setup");
+ createOrAttach.mutate(
+ {
+ paneId,
+ tabId,
+ workspaceId: setup.workspaceId,
+ initialCommands: setup.initialCommands,
+ },
+ {
+ onSuccess: () => {
+ onComplete();
+ },
+ onError: (error) => {
+ console.error(
+ "[WorkspaceInitEffects] Failed to create terminal:",
+ error,
+ );
+ toast.error("Failed to create terminal", {
+ description:
+ error.message || "Terminal setup failed. Please try again.",
+ action: {
+ label: "Open Terminal",
+ onClick: () => {
+ // Allow user to manually trigger terminal creation
+ const { tabId: newTabId, paneId: newPaneId } = addTab(
+ setup.workspaceId,
+ );
+ createOrAttach.mutate({
+ paneId: newPaneId,
+ tabId: newTabId,
+ workspaceId: setup.workspaceId,
+ initialCommands: setup.initialCommands ?? undefined,
+ });
+ },
+ },
+ });
+ // Still complete to prevent infinite retries
+ onComplete();
+ },
+ },
+ );
+ } else {
+ // Show config toast if no setup commands
+ toast.info("No setup script configured", {
+ description: "Automate workspace setup with a config.json file",
+ action: {
+ label: "Configure",
+ onClick: () => openConfigModal(setup.projectId),
+ },
+ onDismiss: () => {
+ dismissConfigToast.mutate({ projectId: setup.projectId });
+ },
+ });
+ onComplete();
+ }
+ },
+ [
+ addTab,
+ setTabAutoTitle,
+ createOrAttach,
+ openConfigModal,
+ dismissConfigToast,
+ ],
+ );
+
+ useEffect(() => {
+ // Process pending setups that have reached ready state
+ for (const [workspaceId, setup] of Object.entries(pendingTerminalSetups)) {
+ const progress = initProgress[workspaceId];
+
+ // Skip if already being processed
+ if (processingRef.current.has(workspaceId)) {
+ continue;
+ }
+
+ // Create terminal when workspace becomes ready
+ if (progress?.step === "ready") {
+ // Mark as processing to prevent duplicate handling
+ processingRef.current.add(workspaceId);
+
+ handleTerminalSetup(setup, () => {
+ // Only remove from pending after successful handling
+ removePendingTerminalSetup(workspaceId);
+ clearProgress(workspaceId);
+ processingRef.current.delete(workspaceId);
+ });
+ }
+
+ // Clean up pending if failed (user will use retry or delete)
+ // Note: losing pending data is OK now - we fetch on demand when ready
+ if (progress?.step === "failed") {
+ removePendingTerminalSetup(workspaceId);
+ }
+ }
+
+ // Handle workspaces that became ready without pending setup data
+ // (e.g., after retry or app restart during init)
+ for (const [workspaceId, progress] of Object.entries(initProgress)) {
+ // Only process ready workspaces that don't have pending setup
+ if (progress.step !== "ready") {
+ continue;
+ }
+ if (pendingTerminalSetups[workspaceId]) {
+ continue; // Already handled above
+ }
+ if (processingRef.current.has(workspaceId)) {
+ continue;
+ }
+
+ // Mark as processing and fetch setup commands from backend
+ processingRef.current.add(workspaceId);
+
+ utils.workspaces.getSetupCommands
+ .fetch({ workspaceId })
+ .then((setupData) => {
+ if (!setupData) {
+ // Workspace not found or no project - just clear progress
+ clearProgress(workspaceId);
+ processingRef.current.delete(workspaceId);
+ return;
+ }
+
+ // Create a pending setup from fetched data and handle it
+ const fetchedSetup: PendingTerminalSetup = {
+ workspaceId,
+ projectId: setupData.projectId,
+ initialCommands: setupData.initialCommands,
+ };
+
+ handleTerminalSetup(fetchedSetup, () => {
+ clearProgress(workspaceId);
+ processingRef.current.delete(workspaceId);
+ });
+ })
+ .catch((error) => {
+ console.error(
+ "[WorkspaceInitEffects] Failed to fetch setup commands:",
+ error,
+ );
+ // Still clear progress to avoid being stuck
+ clearProgress(workspaceId);
+ processingRef.current.delete(workspaceId);
+ });
+ }
+ }, [
+ initProgress,
+ pendingTerminalSetups,
+ removePendingTerminalSetup,
+ clearProgress,
+ handleTerminalSetup,
+ utils.workspaces.getSetupCommands,
+ ]);
+
+ // Renderless component
+ return null;
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/PortsList.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/PortsList/PortsList.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/PortsList.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/PortsList/PortsList.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/PortsList/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/PortsList/index.ts
new file mode 100644
index 000000000..7f04b50ce
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/PortsList/index.ts
@@ -0,0 +1 @@
+export { PortsList } from "./PortsList";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx
new file mode 100644
index 000000000..64bfb4671
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectHeader.tsx
@@ -0,0 +1,104 @@
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuSeparator,
+ ContextMenuTrigger,
+} from "@superset/ui/context-menu";
+import { toast } from "@superset/ui/sonner";
+import { cn } from "@superset/ui/utils";
+import { LuFolderOpen, LuSettings, LuX } from "react-icons/lu";
+import { trpc } from "renderer/lib/trpc";
+import { useOpenSettings } from "renderer/stores/app-state";
+
+interface ProjectHeaderProps {
+ projectId: string;
+ projectName: string;
+ mainRepoPath: string;
+ isCollapsed: boolean;
+ onToggleCollapse: () => void;
+ workspaceCount: number;
+}
+
+export function ProjectHeader({
+ projectId,
+ projectName,
+ mainRepoPath,
+ isCollapsed,
+ onToggleCollapse,
+ workspaceCount,
+}: ProjectHeaderProps) {
+ const utils = trpc.useUtils();
+ const openSettings = useOpenSettings();
+
+ const closeProject = trpc.projects.close.useMutation({
+ onSuccess: (data) => {
+ utils.workspaces.getAllGrouped.invalidate();
+ utils.workspaces.getActive.invalidate();
+ utils.projects.getRecents.invalidate();
+ if (data.terminalWarning) {
+ toast.warning(data.terminalWarning);
+ }
+ },
+ onError: (error) => {
+ toast.error(`Failed to close project: ${error.message}`);
+ },
+ });
+
+ const openInFinder = trpc.external.openInFinder.useMutation({
+ onError: (error) => toast.error(`Failed to open: ${error.message}`),
+ });
+
+ const handleCloseProject = () => {
+ closeProject.mutate({ id: projectId });
+ };
+
+ const handleOpenInFinder = () => {
+ openInFinder.mutate(mainRepoPath);
+ };
+
+ const handleOpenSettings = () => {
+ openSettings("project");
+ };
+
+ return (
+
+
+
+
+
+
+
+ Open in Finder
+
+
+
+ Project Settings
+
+
+
+
+ {closeProject.isPending ? "Closing..." : "Close Project"}
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx
new file mode 100644
index 000000000..a0e4152f9
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx
@@ -0,0 +1,144 @@
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@superset/ui/dropdown-menu";
+import { toast } from "@superset/ui/sonner";
+import { AnimatePresence, motion } from "framer-motion";
+import { useState } from "react";
+import { HiMiniPlus, HiOutlineBolt } from "react-icons/hi2";
+import { useCreateWorkspace } from "renderer/react-query/workspaces";
+import { useWorkspaceSidebarStore } from "renderer/stores";
+import { useOpenNewWorkspaceModal } from "renderer/stores/new-workspace-modal";
+import { WorkspaceListItem } from "../WorkspaceListItem";
+import { ProjectHeader } from "./ProjectHeader";
+
+interface Workspace {
+ id: string;
+ projectId: string;
+ worktreePath: string;
+ type: "worktree" | "branch";
+ branch: string;
+ name: string;
+ tabOrder: number;
+ isUnread: boolean;
+}
+
+interface ProjectSectionProps {
+ projectId: string;
+ projectName: string;
+ mainRepoPath: string;
+ workspaces: Workspace[];
+ activeWorkspaceId: string | null;
+ /** Base index for keyboard shortcuts (0-based) */
+ shortcutBaseIndex: number;
+}
+
+export function ProjectSection({
+ projectId,
+ projectName,
+ mainRepoPath,
+ workspaces,
+ activeWorkspaceId,
+ shortcutBaseIndex,
+}: ProjectSectionProps) {
+ const [dropdownOpen, setDropdownOpen] = useState(false);
+ const { isProjectCollapsed, toggleProjectCollapsed } =
+ useWorkspaceSidebarStore();
+ const createWorkspace = useCreateWorkspace();
+ const openModal = useOpenNewWorkspaceModal();
+
+ const isCollapsed = isProjectCollapsed(projectId);
+
+ const handleQuickCreate = () => {
+ setDropdownOpen(false);
+ toast.promise(createWorkspace.mutateAsync({ projectId }), {
+ loading: "Creating workspace...",
+ success: "Workspace created",
+ error: (err) =>
+ err instanceof Error ? err.message : "Failed to create workspace",
+ });
+ };
+
+ const handleNewWorkspace = () => {
+ setDropdownOpen(false);
+ openModal(projectId);
+ };
+
+ return (
+
+
toggleProjectCollapsed(projectId)}
+ workspaceCount={workspaces.length}
+ />
+
+
+ {!isCollapsed && (
+
+
+ {workspaces.map((workspace, index) => (
+
+ ))}
+
+
+
+
+
+
+
+ New Workspace
+
+
+
+ Quick Create
+
+
+
+
+
+ )}
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/index.ts
new file mode 100644
index 000000000..2111af01d
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/index.ts
@@ -0,0 +1,2 @@
+export { ProjectHeader } from "./ProjectHeader";
+export { ProjectSection } from "./ProjectSection";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ResizableWorkspaceSidebar.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ResizableWorkspaceSidebar.tsx
new file mode 100644
index 000000000..526fa283d
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ResizableWorkspaceSidebar.tsx
@@ -0,0 +1,94 @@
+import { cn } from "@superset/ui/utils";
+import { useCallback, useEffect, useRef } from "react";
+import {
+ MAX_WORKSPACE_SIDEBAR_WIDTH,
+ MIN_WORKSPACE_SIDEBAR_WIDTH,
+ useWorkspaceSidebarStore,
+} from "renderer/stores";
+import { WorkspaceSidebar } from "./WorkspaceSidebar";
+
+export function ResizableWorkspaceSidebar() {
+ const { isOpen, width, setWidth, isResizing, setIsResizing } =
+ useWorkspaceSidebarStore();
+
+ const startXRef = useRef(0);
+ const startWidthRef = useRef(0);
+
+ const handleMouseDown = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ startXRef.current = e.clientX;
+ startWidthRef.current = width;
+ setIsResizing(true);
+ },
+ [width, setIsResizing],
+ );
+
+ const handleMouseMove = useCallback(
+ (e: MouseEvent) => {
+ if (!isResizing) return;
+
+ const delta = e.clientX - startXRef.current;
+ const newWidth = startWidthRef.current + delta;
+ const clampedWidth = Math.max(
+ MIN_WORKSPACE_SIDEBAR_WIDTH,
+ Math.min(MAX_WORKSPACE_SIDEBAR_WIDTH, newWidth),
+ );
+ setWidth(clampedWidth);
+ },
+ [isResizing, setWidth],
+ );
+
+ const handleMouseUp = useCallback(() => {
+ if (isResizing) {
+ setIsResizing(false);
+ }
+ }, [isResizing, setIsResizing]);
+
+ useEffect(() => {
+ if (isResizing) {
+ document.addEventListener("mousemove", handleMouseMove);
+ document.addEventListener("mouseup", handleMouseUp);
+ document.body.style.userSelect = "none";
+ document.body.style.cursor = "col-resize";
+ }
+
+ return () => {
+ document.removeEventListener("mousemove", handleMouseMove);
+ document.removeEventListener("mouseup", handleMouseUp);
+ document.body.style.userSelect = "";
+ document.body.style.cursor = "";
+ };
+ }, [isResizing, handleMouseMove, handleMouseUp]);
+
+ if (!isOpen) {
+ return null;
+ }
+
+ return (
+
+
+
+ {/* Resize handle */}
+ {/* biome-ignore lint/a11y/useSemanticElements:
is not appropriate for interactive resize handles */}
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceDiffStats.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceDiffStats.tsx
new file mode 100644
index 000000000..a2758d40b
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceDiffStats.tsx
@@ -0,0 +1,16 @@
+interface WorkspaceDiffStatsProps {
+ additions: number;
+ deletions: number;
+}
+
+export function WorkspaceDiffStats({
+ additions,
+ deletions,
+}: WorkspaceDiffStatsProps) {
+ return (
+
+ +{additions}
+ -{deletions}
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
new file mode 100644
index 000000000..81340b17e
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
@@ -0,0 +1,380 @@
+import { Button } from "@superset/ui/button";
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuSeparator,
+ ContextMenuTrigger,
+} from "@superset/ui/context-menu";
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@superset/ui/hover-card";
+import { Input } from "@superset/ui/input";
+import { toast } from "@superset/ui/sonner";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { cn } from "@superset/ui/utils";
+import { useState } from "react";
+import { useDrag, useDrop } from "react-dnd";
+import { HiMiniXMark } from "react-icons/hi2";
+import { LuEye, LuEyeOff, LuGitBranch } from "react-icons/lu";
+import { trpc } from "renderer/lib/trpc";
+import {
+ useReorderWorkspaces,
+ useSetActiveWorkspace,
+ useWorkspaceDeleteHandler,
+} from "renderer/react-query/workspaces";
+import { useWorkspaceRename } from "renderer/screens/main/hooks/useWorkspaceRename";
+import { useCloseWorkspacesList } from "renderer/stores/app-state";
+import { useTabsStore } from "renderer/stores/tabs/store";
+import { extractPaneIdsFromLayout } from "renderer/stores/tabs/utils";
+import {
+ BranchSwitcher,
+ DeleteWorkspaceDialog,
+ WorkspaceHoverCardContent,
+} from "./components";
+import {
+ GITHUB_STATUS_STALE_TIME,
+ HOVER_CARD_CLOSE_DELAY,
+ HOVER_CARD_OPEN_DELAY,
+ MAX_KEYBOARD_SHORTCUT_INDEX,
+} from "./constants";
+import { WorkspaceDiffStats } from "./WorkspaceDiffStats";
+import { WorkspaceStatusBadge } from "./WorkspaceStatusBadge";
+
+const WORKSPACE_TYPE = "WORKSPACE";
+
+interface WorkspaceListItemProps {
+ id: string;
+ projectId: string;
+ worktreePath: string;
+ name: string;
+ branch: string;
+ type: "worktree" | "branch";
+ isActive: boolean;
+ isUnread?: boolean;
+ index: number;
+ shortcutIndex?: number;
+}
+
+export function WorkspaceListItem({
+ id,
+ projectId,
+ worktreePath,
+ name,
+ branch,
+ type,
+ isActive,
+ isUnread = false,
+ index,
+ shortcutIndex,
+}: WorkspaceListItemProps) {
+ const isBranchWorkspace = type === "branch";
+ const setActiveWorkspace = useSetActiveWorkspace();
+ const reorderWorkspaces = useReorderWorkspaces();
+ const closeWorkspacesList = useCloseWorkspacesList();
+ const [hasHovered, setHasHovered] = useState(false);
+ const rename = useWorkspaceRename(id, name);
+ const tabs = useTabsStore((s) => s.tabs);
+ const panes = useTabsStore((s) => s.panes);
+ const clearWorkspaceAttention = useTabsStore(
+ (s) => s.clearWorkspaceAttention,
+ );
+ const utils = trpc.useUtils();
+ const openInFinder = trpc.external.openInFinder.useMutation({
+ onError: (error) => toast.error(`Failed to open: ${error.message}`),
+ });
+ const setUnread = trpc.workspaces.setUnread.useMutation({
+ onSuccess: () => {
+ utils.workspaces.getAllGrouped.invalidate();
+ },
+ onError: (error) =>
+ toast.error(`Failed to update unread status: ${error.message}`),
+ });
+
+ // Shared delete logic
+ const { showDeleteDialog, setShowDeleteDialog, handleDeleteClick } =
+ useWorkspaceDeleteHandler();
+
+ // Lazy-load GitHub status on hover to avoid N+1 queries
+ const { data: githubStatus } = trpc.workspaces.getGitHubStatus.useQuery(
+ { workspaceId: id },
+ {
+ enabled: hasHovered && type === "worktree",
+ staleTime: GITHUB_STATUS_STALE_TIME,
+ },
+ );
+
+ // Check if any pane in tabs belonging to this workspace needs attention (agent notifications)
+ const workspaceTabs = tabs.filter((t) => t.workspaceId === id);
+ const workspacePaneIds = new Set(
+ workspaceTabs.flatMap((t) => extractPaneIdsFromLayout(t.layout)),
+ );
+ const hasPaneAttention = Object.values(panes)
+ .filter((p) => p != null && workspacePaneIds.has(p.id))
+ .some((p) => p.needsAttention);
+
+ // Show indicator if workspace is manually marked as unread OR has pane-level attention
+ const needsAttention = isUnread || hasPaneAttention;
+
+ const handleClick = () => {
+ if (!rename.isRenaming) {
+ setActiveWorkspace.mutate({ id });
+ clearWorkspaceAttention(id);
+ // Close workspaces list view if open, to show the workspace's terminal view
+ closeWorkspacesList();
+ }
+ };
+
+ const handleMouseEnter = () => {
+ if (!hasHovered) {
+ setHasHovered(true);
+ }
+ };
+
+ const handleOpenInFinder = () => {
+ if (worktreePath) {
+ openInFinder.mutate(worktreePath);
+ }
+ };
+
+ const handleToggleUnread = () => {
+ setUnread.mutate({ id, isUnread: !isUnread });
+ };
+
+ // Drag and drop
+ const [{ isDragging }, drag] = useDrag(
+ () => ({
+ type: WORKSPACE_TYPE,
+ item: { id, projectId, index },
+ collect: (monitor) => ({
+ isDragging: monitor.isDragging(),
+ }),
+ }),
+ [id, projectId, index],
+ );
+
+ const [, drop] = useDrop({
+ accept: WORKSPACE_TYPE,
+ hover: (item: { id: string; projectId: string; index: number }) => {
+ if (item.projectId === projectId && item.index !== index) {
+ reorderWorkspaces.mutate(
+ {
+ projectId,
+ fromIndex: item.index,
+ toIndex: index,
+ },
+ {
+ onError: (error) =>
+ toast.error(`Failed to reorder workspace: ${error.message}`),
+ },
+ );
+ item.index = index;
+ }
+ },
+ });
+
+ const pr = githubStatus?.pr;
+ const showDiffStats = pr && (pr.additions > 0 || pr.deletions > 0);
+
+ const content = (
+
+ );
+
+ const unreadMenuItem = (
+
+ {isUnread ? (
+ <>
+
+ Mark as Read
+ >
+ ) : (
+ <>
+
+ Mark as Unread
+ >
+ )}
+
+ );
+
+ // Wrap with context menu and hover card
+ if (isBranchWorkspace) {
+ return (
+ <>
+
+ {content}
+
+
+ Open in Finder
+
+
+ {unreadMenuItem}
+
+
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+
+
+ {content}
+
+
+
+ Rename
+
+
+
+ Open in Finder
+
+
+ {unreadMenuItem}
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceStatusBadge.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceStatusBadge.tsx
new file mode 100644
index 000000000..d6eb509d7
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceStatusBadge.tsx
@@ -0,0 +1,53 @@
+import { cn } from "@superset/ui/utils";
+import { LuCircleDot, LuGitMerge, LuGitPullRequest } from "react-icons/lu";
+
+type PRState = "open" | "merged" | "closed" | "draft";
+
+interface WorkspaceStatusBadgeProps {
+ state: PRState;
+ prNumber?: number;
+}
+
+export function WorkspaceStatusBadge({
+ state,
+ prNumber,
+}: WorkspaceStatusBadgeProps) {
+ const iconClass = "w-3 h-3";
+
+ const config = {
+ open: {
+ icon: ,
+ bgColor: "bg-emerald-500/10",
+ },
+ merged: {
+ icon: ,
+ bgColor: "bg-purple-500/10",
+ },
+ closed: {
+ icon: ,
+ bgColor: "bg-destructive/10",
+ },
+ draft: {
+ icon: (
+
+ ),
+ bgColor: "bg-muted",
+ },
+ };
+
+ const { icon, bgColor } = config[state];
+
+ return (
+
+ {icon}
+ {prNumber && (
+ #{prNumber}
+ )}
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/BranchSwitcher/BranchSwitcher.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/BranchSwitcher/BranchSwitcher.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/BranchSwitcher/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/BranchSwitcher/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/DeleteWorkspaceDialog/DeleteWorkspaceDialog.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/DeleteWorkspaceDialog/DeleteWorkspaceDialog.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/DeleteWorkspaceDialog/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/DeleteWorkspaceDialog/index.ts
new file mode 100644
index 000000000..369ca7198
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/DeleteWorkspaceDialog/index.ts
@@ -0,0 +1 @@
+export { DeleteWorkspaceDialog } from "./DeleteWorkspaceDialog";
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/WorkspaceHoverCard.tsx
similarity index 92%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/WorkspaceHoverCard.tsx
index 9d6874045..88f57988d 100644
--- a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/WorkspaceHoverCard.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/WorkspaceHoverCard.tsx
@@ -7,6 +7,7 @@ import {
LuTriangleAlert,
} from "react-icons/lu";
import { trpc } from "renderer/lib/trpc";
+import { usePRStatus } from "renderer/screens/main/hooks";
import { ChecksList } from "./components/ChecksList";
import { ChecksSummary } from "./components/ChecksSummary";
import { PRStatusBadge } from "./components/PRStatusBadge";
@@ -26,13 +27,13 @@ export function WorkspaceHoverCardContent({
{ enabled: !!workspaceId },
);
- const { data: githubStatus, isLoading: isLoadingGithub } =
- trpc.workspaces.getGitHubStatus.useQuery(
- { workspaceId },
- { enabled: !!workspaceId },
- );
+ const {
+ pr,
+ repoUrl,
+ branchExistsOnRemote,
+ isLoading: isLoadingGithub,
+ } = usePRStatus({ workspaceId });
- const pr = githubStatus?.pr;
const needsRebase = worktreeInfo?.gitStatus?.needsRebase;
const worktreeName = worktreeInfo?.worktreeName;
@@ -51,9 +52,9 @@ export function WorkspaceHoverCardContent({
Branch
- {githubStatus?.repoUrl && githubStatus.branchExistsOnRemote ? (
+ {repoUrl && branchExistsOnRemote ? (
- ) : githubStatus ? (
+ ) : repoUrl ? (
No PR for this branch
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/ChecksList.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/CheckItemRow.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/components/CheckItemRow/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksList/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksList/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksSummary/ChecksSummary.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksSummary/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ChecksSummary/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ChecksSummary/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/PRStatusBadge/PRStatusBadge.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/PRStatusBadge/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/PRStatusBadge/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/PRStatusBadge/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ReviewStatus/ReviewStatus.tsx
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ReviewStatus/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/components/ReviewStatus/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/components/ReviewStatus/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/index.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceHoverCard/index.ts
rename to apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/WorkspaceHoverCard/index.ts
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/index.ts
new file mode 100644
index 000000000..282bf9f9a
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/index.ts
@@ -0,0 +1,3 @@
+export { BranchSwitcher } from "./BranchSwitcher";
+export { DeleteWorkspaceDialog } from "./DeleteWorkspaceDialog";
+export { WorkspaceHoverCardContent } from "./WorkspaceHoverCard";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/constants.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/constants.ts
new file mode 100644
index 000000000..b6768dfb7
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/constants.ts
@@ -0,0 +1,15 @@
+/**
+ * Constants for workspace list item behavior
+ */
+
+/** Maximum index for keyboard shortcuts (Cmd+1 through Cmd+9) */
+export const MAX_KEYBOARD_SHORTCUT_INDEX = 9;
+
+/** Stale time for GitHub status queries (30 seconds) */
+export const GITHUB_STATUS_STALE_TIME = 30_000;
+
+/** Delay before showing hover card (ms) */
+export const HOVER_CARD_OPEN_DELAY = 400;
+
+/** Delay before hiding hover card (ms) */
+export const HOVER_CARD_CLOSE_DELAY = 100;
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/index.ts
new file mode 100644
index 000000000..4dd9ef18a
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/index.ts
@@ -0,0 +1,3 @@
+export { WorkspaceDiffStats } from "./WorkspaceDiffStats";
+export { WorkspaceListItem } from "./WorkspaceListItem";
+export { WorkspaceStatusBadge } from "./WorkspaceStatusBadge";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx
new file mode 100644
index 000000000..4ffc0650a
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx
@@ -0,0 +1,54 @@
+import { useMemo } from "react";
+import { useWorkspaceShortcuts } from "renderer/hooks/useWorkspaceShortcuts";
+import { PortsList } from "./PortsList";
+import { ProjectSection } from "./ProjectSection";
+import { WorkspaceSidebarFooter } from "./WorkspaceSidebarFooter";
+import { WorkspaceSidebarHeader } from "./WorkspaceSidebarHeader";
+
+export function WorkspaceSidebar() {
+ const { groups, activeWorkspaceId } = useWorkspaceShortcuts();
+
+ // Calculate shortcut base indices for each project group using cumulative offsets
+ const projectShortcutIndices = useMemo(
+ () =>
+ groups.reduce<{ indices: number[]; cumulative: number }>(
+ (acc, group) => ({
+ indices: [...acc.indices, acc.cumulative],
+ cumulative: acc.cumulative + group.workspaces.length,
+ }),
+ { indices: [], cumulative: 0 },
+ ).indices,
+ [groups],
+ );
+
+ return (
+
+
+
+
+ {groups.map((group, index) => (
+
+ ))}
+
+ {groups.length === 0 && (
+
+ No workspaces yet
+ Add a project to get started
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarFooter.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarFooter.tsx
new file mode 100644
index 000000000..3a0b6d1c7
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarFooter.tsx
@@ -0,0 +1,62 @@
+import { Button } from "@superset/ui/button";
+import { toast } from "@superset/ui/sonner";
+import { LuFolderOpen } from "react-icons/lu";
+import { useOpenNew } from "renderer/react-query/projects";
+import { useCreateBranchWorkspace } from "renderer/react-query/workspaces";
+
+export function WorkspaceSidebarFooter() {
+ const openNew = useOpenNew();
+ const createBranchWorkspace = useCreateBranchWorkspace();
+
+ const handleOpenNewProject = async () => {
+ try {
+ const result = await openNew.mutateAsync(undefined);
+ if (result.canceled) {
+ return;
+ }
+ if ("error" in result) {
+ toast.error("Failed to open project", {
+ description: result.error,
+ });
+ return;
+ }
+ if ("needsGitInit" in result) {
+ toast.error("Selected folder is not a git repository", {
+ description:
+ "Please use 'Open project' from the start view to initialize git.",
+ });
+ return;
+ }
+ // Create a main workspace on the current branch for the new project
+ toast.promise(
+ createBranchWorkspace.mutateAsync({ projectId: result.project.id }),
+ {
+ loading: "Opening project...",
+ success: "Project opened",
+ error: (err) =>
+ err instanceof Error ? err.message : "Failed to open project",
+ },
+ );
+ } catch (error) {
+ toast.error("Failed to open project", {
+ description:
+ error instanceof Error ? error.message : "An unknown error occurred",
+ });
+ }
+ };
+
+ return (
+
+
+
+ Add repository
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/NewWorkspaceButton.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/NewWorkspaceButton.tsx
new file mode 100644
index 000000000..0f392fe62
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/NewWorkspaceButton.tsx
@@ -0,0 +1,30 @@
+import { LuPlus } from "react-icons/lu";
+import { trpc } from "renderer/lib/trpc";
+import { useOpenNewWorkspaceModal } from "renderer/stores/new-workspace-modal";
+
+export function NewWorkspaceButton() {
+ const openModal = useOpenNewWorkspaceModal();
+ const { data: activeWorkspace, isLoading } =
+ trpc.workspaces.getActive.useQuery();
+
+ const handleClick = () => {
+ // projectId may be undefined if no workspace is active or query failed
+ // openModal handles undefined by opening without a pre-selected project
+ const projectId = activeWorkspace?.projectId;
+ openModal(projectId);
+ };
+
+ return (
+
+
+
+
+ New Workspace
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx
new file mode 100644
index 000000000..9103919cf
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx
@@ -0,0 +1,45 @@
+import { cn } from "@superset/ui/utils";
+import { LuLayers } from "react-icons/lu";
+import {
+ useCloseWorkspacesList,
+ useCurrentView,
+ useOpenWorkspacesList,
+} from "renderer/stores/app-state";
+import { NewWorkspaceButton } from "./NewWorkspaceButton";
+
+export function WorkspaceSidebarHeader() {
+ const currentView = useCurrentView();
+ const openWorkspacesList = useOpenWorkspacesList();
+ const closeWorkspacesList = useCloseWorkspacesList();
+
+ const isWorkspacesListOpen = currentView === "workspaces-list";
+
+ const handleClick = () => {
+ if (isWorkspacesListOpen) {
+ closeWorkspacesList();
+ } else {
+ openWorkspacesList();
+ }
+ };
+
+ return (
+
+
+
+
+
+ Workspaces
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/index.ts
new file mode 100644
index 000000000..844ddc41e
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/index.ts
@@ -0,0 +1 @@
+export { WorkspaceSidebarHeader } from "./WorkspaceSidebarHeader";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/index.ts
new file mode 100644
index 000000000..d8dc22673
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/index.ts
@@ -0,0 +1,2 @@
+export { ResizableWorkspaceSidebar } from "./ResizableWorkspaceSidebar";
+export { WorkspaceSidebar } from "./WorkspaceSidebar";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsx
index abcc51d51..355d70a1c 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsx
@@ -1,16 +1,15 @@
import { DiffEditor, type DiffOnMount } from "@monaco-editor/react";
import type * as Monaco from "monaco-editor";
-import { useCallback, useRef } from "react";
+import { useCallback, useEffect, useRef, useState } from "react";
import { LuLoader } from "react-icons/lu";
import {
+ MONACO_EDITOR_OPTIONS,
+ registerSaveAction,
SUPERSET_THEME,
useMonacoReady,
} from "renderer/contexts/MonacoProvider";
import type { DiffViewMode, FileContents } from "shared/changes-types";
-import {
- registerCopyPathLineAction,
- registerSaveCommand,
-} from "./editor-actions";
+import { registerCopyPathLineAction } from "./editor-actions";
interface DiffViewerProps {
contents: FileContents;
@@ -18,6 +17,7 @@ interface DiffViewerProps {
filePath: string;
editable?: boolean;
onSave?: (content: string) => void;
+ onChange?: (content: string) => void;
}
export function DiffViewer({
@@ -26,17 +26,23 @@ export function DiffViewer({
filePath,
editable = false,
onSave,
+ onChange,
}: DiffViewerProps) {
const isMonacoReady = useMonacoReady();
const modifiedEditorRef = useRef
(
null,
);
+ // Track when editor is mounted to trigger effects at the right time
+ const [isEditorMounted, setIsEditorMounted] = useState(false);
const handleSave = useCallback(() => {
if (!editable || !onSave || !modifiedEditorRef.current) return;
onSave(modifiedEditorRef.current.getValue());
}, [editable, onSave]);
+ // Store disposable for content change listener cleanup
+ const changeListenerRef = useRef(null);
+
const handleMount: DiffOnMount = useCallback(
(editor) => {
const originalEditor = editor.getOriginalEditor();
@@ -46,13 +52,43 @@ export function DiffViewer({
registerCopyPathLineAction(originalEditor, filePath);
registerCopyPathLineAction(modifiedEditor, filePath);
- if (editable) {
- registerSaveCommand(modifiedEditor, handleSave);
- }
+ setIsEditorMounted(true);
},
- [editable, handleSave, filePath],
+ [filePath],
);
+ // Update readOnly and register save action when editable changes or editor mounts
+ // Using addAction with an ID allows replacing the action on subsequent calls
+ useEffect(() => {
+ if (!isEditorMounted || !modifiedEditorRef.current) return;
+
+ modifiedEditorRef.current.updateOptions({ readOnly: !editable });
+
+ if (editable) {
+ registerSaveAction(modifiedEditorRef.current, handleSave);
+ }
+ }, [isEditorMounted, editable, handleSave]);
+
+ // Set up content change listener for dirty tracking
+ useEffect(() => {
+ if (!isEditorMounted || !modifiedEditorRef.current || !onChange) return;
+
+ // Clean up previous listener
+ changeListenerRef.current?.dispose();
+
+ changeListenerRef.current =
+ modifiedEditorRef.current.onDidChangeModelContent(() => {
+ if (modifiedEditorRef.current) {
+ onChange(modifiedEditorRef.current.getValue());
+ }
+ });
+
+ return () => {
+ changeListenerRef.current?.dispose();
+ changeListenerRef.current = null;
+ };
+ }, [isEditorMounted, onChange]);
+
if (!isMonacoReady) {
return (
@@ -78,23 +114,12 @@ export function DiffViewer({
}
options={{
+ ...MONACO_EDITOR_OPTIONS,
renderSideBySide: viewMode === "side-by-side",
readOnly: !editable,
originalEditable: false,
- minimap: { enabled: false },
- scrollBeyondLastLine: false,
renderOverviewRuler: false,
- wordWrap: "on",
diffWordWrap: "on",
- fontSize: 13,
- lineHeight: 20,
- fontFamily:
- "ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace",
- padding: { top: 8, bottom: 8 },
- scrollbar: {
- verticalScrollbarSize: 8,
- horizontalScrollbarSize: 8,
- },
}}
/>
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/editor-actions.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/editor-actions.ts
index 7a5bb9c59..447ffe35f 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/editor-actions.ts
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/editor-actions.ts
@@ -27,10 +27,3 @@ export function registerCopyPathLineAction(
},
});
}
-
-export function registerSaveCommand(
- editor: Monaco.editor.IStandaloneCodeEditor,
- onSave: () => void,
-) {
- editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, onSave);
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ContentHeader/ContentHeader.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ContentHeader/ContentHeader.tsx
new file mode 100644
index 000000000..cbc1deac5
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ContentHeader/ContentHeader.tsx
@@ -0,0 +1,28 @@
+import type { ReactNode } from "react";
+
+interface ContentHeaderProps {
+ /** Optional leading action */
+ leadingAction?: ReactNode;
+ /** Mode-specific header content (e.g., GroupStrip or file info) */
+ children: ReactNode;
+ /** Optional trailing action (e.g., SidebarControl) */
+ trailingAction?: ReactNode;
+}
+
+export function ContentHeader({
+ leadingAction,
+ children,
+ trailingAction,
+}: ContentHeaderProps) {
+ return (
+
+ {leadingAction && (
+
{leadingAction}
+ )}
+
{children}
+ {trailingAction && (
+
{trailingAction}
+ )}
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ContentHeader/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ContentHeader/index.ts
new file mode 100644
index 000000000..26fb6ccbc
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ContentHeader/index.ts
@@ -0,0 +1 @@
+export { ContentHeader } from "./ContentHeader";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/EmptyTabView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/EmptyTabView.tsx
index 0f9a435a8..3d984e3aa 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/EmptyTabView.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/EmptyTabView.tsx
@@ -3,16 +3,16 @@ import { HiMiniCommandLine } from "react-icons/hi2";
import { useHotkeyDisplay } from "renderer/stores/hotkeys";
export function EmptyTabView() {
- const newTerminalDisplay = useHotkeyDisplay("NEW_TERMINAL");
+ const newGroupDisplay = useHotkeyDisplay("NEW_GROUP");
const openInAppDisplay = useHotkeyDisplay("OPEN_IN_APP");
const shortcuts = [
- { label: "New Terminal", display: newTerminalDisplay },
+ { label: "New Tab", display: newGroupDisplay },
{ label: "Open in App", display: openInAppDisplay },
];
return (
-
+
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupItem.tsx
new file mode 100644
index 000000000..5154b0d01
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupItem.tsx
@@ -0,0 +1,79 @@
+import { Button } from "@superset/ui/button";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { cn } from "@superset/ui/utils";
+import { HiMiniXMark } from "react-icons/hi2";
+import type { Tab } from "renderer/stores/tabs/types";
+import { getTabDisplayName } from "renderer/stores/tabs/utils";
+
+interface GroupItemProps {
+ tab: Tab;
+ isActive: boolean;
+ needsAttention: boolean;
+ onSelect: () => void;
+ onClose: () => void;
+}
+
+export function GroupItem({
+ tab,
+ isActive,
+ needsAttention,
+ onSelect,
+ onClose,
+}: GroupItemProps) {
+ const displayName = getTabDisplayName(tab);
+
+ return (
+
+
+
+
+
+ {displayName}
+
+ {needsAttention && (
+
+
+
+
+ )}
+
+
+
+ {displayName}
+
+
+
+
+ {
+ e.stopPropagation();
+ onClose();
+ }}
+ className={cn(
+ "absolute right-1 top-1/2 -translate-y-1/2 cursor-pointer size-5 group-hover:opacity-100",
+ isActive ? "opacity-90" : "opacity-0",
+ )}
+ aria-label="Close group"
+ >
+
+
+
+
+ Close group
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupStrip.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupStrip.tsx
new file mode 100644
index 000000000..54863a005
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupStrip.tsx
@@ -0,0 +1,220 @@
+import type { TerminalPreset } from "@superset/local-db";
+import { Button } from "@superset/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@superset/ui/dropdown-menu";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { useCallback, useMemo, useRef, useState } from "react";
+import {
+ HiMiniChevronDown,
+ HiMiniCog6Tooth,
+ HiMiniCommandLine,
+ HiMiniPlus,
+} from "react-icons/hi2";
+import {
+ getPresetIcon,
+ useIsDarkTheme,
+} from "renderer/assets/app-icons/preset-icons";
+import { HotkeyTooltipContent } from "renderer/components/HotkeyTooltipContent";
+import { trpc } from "renderer/lib/trpc";
+import { usePresets } from "renderer/react-query/presets";
+import { useOpenSettings } from "renderer/stores";
+import { useTabsStore } from "renderer/stores/tabs/store";
+import { GroupItem } from "./GroupItem";
+
+export function GroupStrip() {
+ const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
+ const activeWorkspaceId = activeWorkspace?.id;
+
+ const allTabs = useTabsStore((s) => s.tabs);
+ const panes = useTabsStore((s) => s.panes);
+ const activeTabIds = useTabsStore((s) => s.activeTabIds);
+ const addTab = useTabsStore((s) => s.addTab);
+ const renameTab = useTabsStore((s) => s.renameTab);
+ const removeTab = useTabsStore((s) => s.removeTab);
+ const setActiveTab = useTabsStore((s) => s.setActiveTab);
+
+ const { presets } = usePresets();
+ const isDark = useIsDarkTheme();
+ const openSettings = useOpenSettings();
+ const [dropdownOpen, setDropdownOpen] = useState(false);
+ const hoverTimeoutRef = useRef
| null>(null);
+
+ const handleDropdownMouseEnter = useCallback(() => {
+ if (hoverTimeoutRef.current) {
+ clearTimeout(hoverTimeoutRef.current);
+ }
+ hoverTimeoutRef.current = setTimeout(() => {
+ setDropdownOpen(true);
+ }, 150);
+ }, []);
+
+ const handleDropdownMouseLeave = useCallback(() => {
+ if (hoverTimeoutRef.current) {
+ clearTimeout(hoverTimeoutRef.current);
+ }
+ hoverTimeoutRef.current = setTimeout(() => {
+ setDropdownOpen(false);
+ }, 150);
+ }, []);
+
+ const tabs = useMemo(
+ () =>
+ activeWorkspaceId
+ ? allTabs.filter((tab) => tab.workspaceId === activeWorkspaceId)
+ : [],
+ [activeWorkspaceId, allTabs],
+ );
+
+ const activeTabId = activeWorkspaceId
+ ? activeTabIds[activeWorkspaceId]
+ : null;
+
+ // Check which tabs have panes that need attention
+ const tabsWithAttention = useMemo(() => {
+ const result = new Set();
+ for (const pane of Object.values(panes)) {
+ if (pane.needsAttention) {
+ result.add(pane.tabId);
+ }
+ }
+ return result;
+ }, [panes]);
+
+ const handleAddGroup = () => {
+ if (activeWorkspaceId) {
+ addTab(activeWorkspaceId);
+ }
+ };
+
+ const handleSelectPreset = (preset: TerminalPreset) => {
+ if (!activeWorkspaceId) return;
+
+ const { tabId } = addTab(activeWorkspaceId, {
+ initialCommands: preset.commands,
+ initialCwd: preset.cwd || undefined,
+ });
+
+ if (preset.name) {
+ renameTab(tabId, preset.name);
+ }
+
+ setDropdownOpen(false);
+ };
+
+ const handleOpenPresetsSettings = () => {
+ openSettings("presets");
+ setDropdownOpen(false);
+ };
+
+ const handleSelectGroup = (tabId: string) => {
+ if (activeWorkspaceId) {
+ setActiveTab(activeWorkspaceId, tabId);
+ }
+ };
+
+ const handleCloseGroup = (tabId: string) => {
+ removeTab(tabId);
+ };
+
+ return (
+
+ {tabs.length > 0 && (
+
+ {tabs.map((tab) => (
+
+ handleSelectGroup(tab.id)}
+ onClose={() => handleCloseGroup(tab.id)}
+ />
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {presets.length > 0 && (
+ <>
+ {presets.map((preset) => {
+ const presetIcon = getPresetIcon(preset.name, isDark);
+ return (
+ handleSelectPreset(preset)}
+ className="gap-2"
+ >
+ {presetIcon ? (
+
+ ) : (
+
+ )}
+ {preset.name || "default"}
+
+ );
+ })}
+
+ >
+ )}
+
+
+ Configure Presets
+
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/index.ts
new file mode 100644
index 000000000..e905a6c8b
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/index.ts
@@ -0,0 +1 @@
+export { GroupStrip } from "./GroupStrip";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
new file mode 100644
index 000000000..146be2b36
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
@@ -0,0 +1,304 @@
+import type * as Monaco from "monaco-editor";
+import { useCallback, useEffect, useRef, useState } from "react";
+import type { MosaicBranch } from "react-mosaic-component";
+import { useTabsStore } from "renderer/stores/tabs/store";
+import type { Pane } from "renderer/stores/tabs/types";
+import type { FileViewerMode } from "shared/tabs-types";
+import { BasePaneWindow } from "../components";
+import { FileViewerContent } from "./components/FileViewerContent";
+import { FileViewerToolbar } from "./components/FileViewerToolbar";
+import { useFileContent } from "./hooks/useFileContent";
+import { useFileSave } from "./hooks/useFileSave";
+import { UnsavedChangesDialog } from "./UnsavedChangesDialog";
+
+interface FileViewerPaneProps {
+ paneId: string;
+ path: MosaicBranch[];
+ pane: Pane;
+ isActive: boolean;
+ tabId: string;
+ worktreePath: string;
+ splitPaneAuto: (
+ tabId: string,
+ sourcePaneId: string,
+ dimensions: { width: number; height: number },
+ path?: MosaicBranch[],
+ ) => void;
+ removePane: (paneId: string) => void;
+ setFocusedPane: (tabId: string, paneId: string) => void;
+}
+
+export function FileViewerPane({
+ paneId,
+ path,
+ pane,
+ isActive,
+ tabId,
+ worktreePath,
+ splitPaneAuto,
+ removePane,
+ setFocusedPane,
+}: FileViewerPaneProps) {
+ const editorRef = useRef(null);
+ const [isDirty, setIsDirty] = useState(false);
+ const originalContentRef = useRef("");
+ const draftContentRef = useRef(null);
+ const originalDiffContentRef = useRef("");
+ const currentDiffContentRef = useRef("");
+ const [showUnsavedDialog, setShowUnsavedDialog] = useState(false);
+ const [isSavingAndSwitching, setIsSavingAndSwitching] = useState(false);
+ const pendingModeRef = useRef(null);
+
+ const fileViewer = pane.fileViewer;
+ const filePath = fileViewer?.filePath ?? "";
+ const viewMode = fileViewer?.viewMode ?? "raw";
+ const isLocked = fileViewer?.isLocked ?? false;
+ const diffCategory = fileViewer?.diffCategory;
+ const commitHash = fileViewer?.commitHash;
+ const oldPath = fileViewer?.oldPath;
+ const initialLine = fileViewer?.initialLine;
+ const initialColumn = fileViewer?.initialColumn;
+
+ const { handleSaveRaw, handleSaveDiff, isSaving } = useFileSave({
+ worktreePath,
+ filePath,
+ paneId,
+ diffCategory,
+ editorRef,
+ originalContentRef,
+ originalDiffContentRef,
+ draftContentRef,
+ setIsDirty,
+ });
+
+ const { rawFileData, isLoadingRaw, diffData, isLoadingDiff } = useFileContent(
+ {
+ worktreePath,
+ filePath,
+ viewMode,
+ diffCategory,
+ commitHash,
+ oldPath,
+ isDirty,
+ originalContentRef,
+ originalDiffContentRef,
+ },
+ );
+
+ const handleEditorChange = useCallback((value: string | undefined) => {
+ if (value === undefined) return;
+ if (originalContentRef.current === "") {
+ originalContentRef.current = value;
+ return;
+ }
+ setIsDirty(value !== originalContentRef.current);
+ }, []);
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Reset on file change only
+ useEffect(() => {
+ setIsDirty(false);
+ originalContentRef.current = "";
+ draftContentRef.current = null;
+ }, [filePath]);
+
+ const handleDiffChange = useCallback((content: string) => {
+ currentDiffContentRef.current = content;
+ if (originalDiffContentRef.current === "") {
+ originalDiffContentRef.current = content;
+ return;
+ }
+ setIsDirty(content !== originalDiffContentRef.current);
+ }, []);
+
+ if (!fileViewer) {
+ return (
+ }
+ >
+
+ No file viewer state
+
+
+ );
+ }
+
+ const handleToggleLock = () => {
+ const panes = useTabsStore.getState().panes;
+ const currentPane = panes[paneId];
+ if (currentPane?.fileViewer) {
+ useTabsStore.setState({
+ panes: {
+ ...panes,
+ [paneId]: {
+ ...currentPane,
+ fileViewer: {
+ ...currentPane.fileViewer,
+ isLocked: !currentPane.fileViewer.isLocked,
+ },
+ },
+ },
+ });
+ }
+ };
+
+ const switchToMode = (newMode: FileViewerMode) => {
+ const panes = useTabsStore.getState().panes;
+ const currentPane = panes[paneId];
+ if (currentPane?.fileViewer) {
+ useTabsStore.setState({
+ panes: {
+ ...panes,
+ [paneId]: {
+ ...currentPane,
+ fileViewer: {
+ ...currentPane.fileViewer,
+ viewMode: newMode,
+ },
+ },
+ },
+ });
+ }
+ };
+
+ const handleViewModeChange = (value: string) => {
+ if (!value) return;
+ const newMode = value as FileViewerMode;
+
+ if (isDirty && newMode !== viewMode) {
+ pendingModeRef.current = newMode;
+ setShowUnsavedDialog(true);
+ return;
+ }
+
+ switchToMode(newMode);
+ };
+
+ const handleSaveAndSwitch = async () => {
+ if (!pendingModeRef.current) return;
+
+ setIsSavingAndSwitching(true);
+ try {
+ if (viewMode === "raw" && editorRef.current) {
+ const savedContent = editorRef.current.getValue();
+ await handleSaveRaw();
+ originalContentRef.current = savedContent;
+ originalDiffContentRef.current = "";
+ } else if (
+ viewMode === "diff" &&
+ currentDiffContentRef.current !== undefined
+ ) {
+ const savedContent = currentDiffContentRef.current;
+ await handleSaveDiff(savedContent);
+ originalDiffContentRef.current = savedContent;
+ originalContentRef.current = "";
+ }
+
+ setIsDirty(false);
+ draftContentRef.current = null;
+ currentDiffContentRef.current = "";
+
+ switchToMode(pendingModeRef.current);
+ pendingModeRef.current = null;
+ setShowUnsavedDialog(false);
+ } catch (error) {
+ console.error("[FileViewerPane] Save failed:", error);
+ } finally {
+ setIsSavingAndSwitching(false);
+ }
+ };
+
+ const handleDiscardAndSwitch = () => {
+ if (!pendingModeRef.current) return;
+
+ if (viewMode === "raw" && editorRef.current) {
+ editorRef.current.setValue(originalContentRef.current);
+ }
+
+ setIsDirty(false);
+ draftContentRef.current = null;
+ currentDiffContentRef.current = "";
+
+ switchToMode(pendingModeRef.current);
+ pendingModeRef.current = null;
+ };
+
+ const fileName = filePath.split("/").pop() || filePath;
+ const isMarkdown =
+ filePath.endsWith(".md") ||
+ filePath.endsWith(".markdown") ||
+ filePath.endsWith(".mdx");
+ const hasDiff = !!diffCategory;
+ const hasDraft = draftContentRef.current !== null;
+ const isDiffEditable =
+ (diffCategory === "staged" || diffCategory === "unstaged") && !hasDraft;
+ const showEditableBadge =
+ viewMode === "raw" || (viewMode === "diff" && isDiffEditable);
+
+ return (
+ <>
+ (
+
+
+
+ )}
+ >
+
+
+
+ >
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/UnsavedChangesDialog.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/UnsavedChangesDialog.tsx
new file mode 100644
index 000000000..98c47db44
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/UnsavedChangesDialog.tsx
@@ -0,0 +1,74 @@
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@superset/ui/alert-dialog";
+import { Button } from "@superset/ui/button";
+import { LuLoader } from "react-icons/lu";
+
+interface UnsavedChangesDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ onSaveAndSwitch: () => void;
+ onDiscardAndSwitch: () => void;
+ isSaving?: boolean;
+}
+
+export function UnsavedChangesDialog({
+ open,
+ onOpenChange,
+ onSaveAndSwitch,
+ onDiscardAndSwitch,
+ isSaving = false,
+}: UnsavedChangesDialogProps) {
+ const handleSaveAndSwitch = (e: React.MouseEvent) => {
+ e.preventDefault();
+ onSaveAndSwitch();
+ // Don't close dialog - parent will close on success
+ };
+
+ const handleDiscardAndSwitch = (e: React.MouseEvent) => {
+ e.preventDefault();
+ onDiscardAndSwitch();
+ onOpenChange(false);
+ };
+
+ return (
+
+
+
+ Unsaved Changes
+
+ You have unsaved changes. What would you like to do?
+
+
+
+ Cancel
+
+ Discard & Switch
+
+
+ {isSaving ? (
+ <>
+
+ Saving...
+ >
+ ) : (
+ "Save & Switch"
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx
new file mode 100644
index 000000000..f6945203d
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx
@@ -0,0 +1,232 @@
+import Editor, { type OnMount } from "@monaco-editor/react";
+import type * as Monaco from "monaco-editor";
+import { type MutableRefObject, useCallback, useEffect, useRef } from "react";
+import { LuLoader } from "react-icons/lu";
+import { MarkdownRenderer } from "renderer/components/MarkdownRenderer";
+import {
+ MONACO_EDITOR_OPTIONS,
+ registerSaveAction,
+ SUPERSET_THEME,
+ useMonacoReady,
+} from "renderer/contexts/MonacoProvider";
+import { detectLanguage } from "shared/detect-language";
+import type { FileViewerMode } from "shared/tabs-types";
+import { DiffViewer } from "../../../../../ChangesContent/components/DiffViewer";
+
+interface RawFileData {
+ ok: true;
+ content: string;
+}
+
+interface RawFileError {
+ ok: false;
+ reason:
+ | "too-large"
+ | "binary"
+ | "outside-worktree"
+ | "symlink-escape"
+ | "not-found";
+}
+
+type RawFileResult = RawFileData | RawFileError | undefined;
+
+interface DiffData {
+ original: string;
+ modified: string;
+ language: string;
+}
+
+interface FileViewerContentProps {
+ viewMode: FileViewerMode;
+ filePath: string;
+ isLoadingRaw: boolean;
+ isLoadingDiff: boolean;
+ rawFileData: RawFileResult;
+ diffData: DiffData | undefined;
+ isDiffEditable: boolean;
+ editorRef: MutableRefObject;
+ originalContentRef: MutableRefObject;
+ draftContentRef: MutableRefObject;
+ initialLine?: number;
+ initialColumn?: number;
+ onSaveRaw: () => Promise;
+ onSaveDiff?: (content: string) => Promise;
+ onEditorChange: (value: string | undefined) => void;
+ onDiffChange?: (content: string) => void;
+ setIsDirty: (dirty: boolean) => void;
+}
+
+export function FileViewerContent({
+ viewMode,
+ filePath,
+ isLoadingRaw,
+ isLoadingDiff,
+ rawFileData,
+ diffData,
+ isDiffEditable,
+ editorRef,
+ originalContentRef,
+ draftContentRef,
+ initialLine,
+ initialColumn,
+ onSaveRaw,
+ onSaveDiff,
+ onEditorChange,
+ onDiffChange,
+ setIsDirty,
+}: FileViewerContentProps) {
+ const isMonacoReady = useMonacoReady();
+ const hasAppliedInitialLocationRef = useRef(false);
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Reset on file change only
+ useEffect(() => {
+ hasAppliedInitialLocationRef.current = false;
+ }, [filePath]);
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Only reset when coordinates change
+ useEffect(() => {
+ hasAppliedInitialLocationRef.current = false;
+ }, [initialLine, initialColumn]);
+
+ const handleEditorMount: OnMount = useCallback(
+ (editor) => {
+ editorRef.current = editor;
+ if (!draftContentRef.current) {
+ originalContentRef.current = editor.getValue();
+ }
+ setIsDirty(editor.getValue() !== originalContentRef.current);
+ registerSaveAction(editor, onSaveRaw);
+ },
+ [onSaveRaw, editorRef, originalContentRef, draftContentRef, setIsDirty],
+ );
+
+ useEffect(() => {
+ if (
+ viewMode !== "raw" ||
+ !editorRef.current ||
+ !initialLine ||
+ hasAppliedInitialLocationRef.current ||
+ isLoadingRaw ||
+ !rawFileData?.ok
+ ) {
+ return;
+ }
+
+ const editor = editorRef.current;
+ const model = editor.getModel();
+ if (!model) return;
+
+ const lineCount = model.getLineCount();
+ const safeLine = Math.max(1, Math.min(initialLine, lineCount));
+ const maxColumn = model.getLineMaxColumn(safeLine);
+ const safeColumn = Math.max(1, Math.min(initialColumn ?? 1, maxColumn));
+
+ const position = { lineNumber: safeLine, column: safeColumn };
+ editor.setPosition(position);
+ editor.revealPositionInCenter(position);
+ editor.focus();
+
+ hasAppliedInitialLocationRef.current = true;
+ }, [
+ viewMode,
+ initialLine,
+ initialColumn,
+ isLoadingRaw,
+ rawFileData,
+ editorRef,
+ ]);
+
+ if (viewMode === "diff") {
+ if (isLoadingDiff) {
+ return (
+
+ Loading diff...
+
+ );
+ }
+ if (!diffData) {
+ return (
+
+ No diff available
+
+ );
+ }
+ return (
+
+ );
+ }
+
+ if (isLoadingRaw) {
+ return (
+
+ Loading...
+
+ );
+ }
+
+ if (!rawFileData?.ok) {
+ const errorMessage =
+ rawFileData?.reason === "too-large"
+ ? "File is too large to preview"
+ : rawFileData?.reason === "binary"
+ ? "Binary file preview not supported"
+ : rawFileData?.reason === "outside-worktree"
+ ? "File is outside worktree"
+ : rawFileData?.reason === "symlink-escape"
+ ? "File is a symlink pointing outside worktree"
+ : "File not found";
+ return (
+
+ {errorMessage}
+
+ );
+ }
+
+ if (viewMode === "rendered") {
+ return (
+
+
+
+ );
+ }
+
+ if (!isMonacoReady) {
+ return (
+
+
+ Loading editor...
+
+ );
+ }
+
+ return (
+
+
+ Loading editor...
+
+ }
+ options={MONACO_EDITOR_OPTIONS}
+ />
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/index.ts
new file mode 100644
index 000000000..15337eaf3
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/index.ts
@@ -0,0 +1 @@
+export { FileViewerContent } from "./FileViewerContent";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerToolbar/FileViewerToolbar.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerToolbar/FileViewerToolbar.tsx
new file mode 100644
index 000000000..7232e304b
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerToolbar/FileViewerToolbar.tsx
@@ -0,0 +1,113 @@
+import { Badge } from "@superset/ui/badge";
+import { ToggleGroup, ToggleGroupItem } from "@superset/ui/toggle-group";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import {
+ HiMiniLockClosed,
+ HiMiniLockOpen,
+ HiMiniPencil,
+} from "react-icons/hi2";
+import type { FileViewerMode } from "shared/tabs-types";
+import { PaneToolbarActions } from "../../../components";
+import type { SplitOrientation } from "../../../hooks";
+
+interface FileViewerToolbarProps {
+ fileName: string;
+ isDirty: boolean;
+ isSaving: boolean;
+ viewMode: FileViewerMode;
+ isLocked: boolean;
+ isMarkdown: boolean;
+ hasDiff: boolean;
+ showEditableBadge: boolean;
+ splitOrientation: SplitOrientation;
+ onViewModeChange: (value: string) => void;
+ onSplitPane: (e: React.MouseEvent) => void;
+ onToggleLock: () => void;
+ onClosePane: (e: React.MouseEvent) => void;
+}
+
+export function FileViewerToolbar({
+ fileName,
+ isDirty,
+ isSaving,
+ viewMode,
+ isLocked,
+ isMarkdown,
+ hasDiff,
+ showEditableBadge,
+ splitOrientation,
+ onViewModeChange,
+ onSplitPane,
+ onToggleLock,
+ onClosePane,
+}: FileViewerToolbarProps) {
+ return (
+
+
+
+ {isDirty && ●}
+ {fileName}
+
+ {showEditableBadge && (
+
+
+ {isSaving ? "Saving..." : "⌘S"}
+
+ )}
+
+
+
+ {isMarkdown && (
+
+ Rendered
+
+ )}
+
+ Raw
+
+ {hasDiff && (
+
+ Diff
+
+ )}
+
+
+
+
+ {isLocked ? (
+
+ ) : (
+
+ )}
+
+
+
+ {isLocked
+ ? "Unlock (allow file replacement)"
+ : "Lock (prevent file replacement)"}
+
+
+ }
+ />
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerToolbar/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerToolbar/index.ts
new file mode 100644
index 000000000..843916763
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerToolbar/index.ts
@@ -0,0 +1 @@
+export { FileViewerToolbar } from "./FileViewerToolbar";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/index.ts
new file mode 100644
index 000000000..ba24b334c
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/index.ts
@@ -0,0 +1 @@
+export { useFileContent } from "./useFileContent";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
new file mode 100644
index 000000000..2c4bbd68f
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
@@ -0,0 +1,79 @@
+import { useEffect } from "react";
+import { trpc } from "renderer/lib/trpc";
+import type { ChangeCategory } from "shared/changes-types";
+
+interface UseFileContentParams {
+ worktreePath: string;
+ filePath: string;
+ viewMode: "raw" | "diff" | "rendered";
+ diffCategory?: ChangeCategory;
+ commitHash?: string;
+ oldPath?: string;
+ isDirty: boolean;
+ originalContentRef: React.MutableRefObject
;
+ originalDiffContentRef: React.MutableRefObject;
+}
+
+export function useFileContent({
+ worktreePath,
+ filePath,
+ viewMode,
+ diffCategory,
+ commitHash,
+ oldPath,
+ isDirty,
+ originalContentRef,
+ originalDiffContentRef,
+}: UseFileContentParams) {
+ const { data: branchData } = trpc.changes.getBranches.useQuery(
+ { worktreePath },
+ { enabled: !!worktreePath && diffCategory === "against-base" },
+ );
+ const effectiveBaseBranch = branchData?.defaultBranch ?? "main";
+
+ const { data: rawFileData, isLoading: isLoadingRaw } =
+ trpc.changes.readWorkingFile.useQuery(
+ { worktreePath, filePath },
+ {
+ enabled: viewMode !== "diff" && !!filePath && !!worktreePath,
+ },
+ );
+
+ const { data: diffData, isLoading: isLoadingDiff } =
+ trpc.changes.getFileContents.useQuery(
+ {
+ worktreePath,
+ filePath,
+ oldPath,
+ category: diffCategory ?? "unstaged",
+ commitHash,
+ defaultBranch:
+ diffCategory === "against-base" ? effectiveBaseBranch : undefined,
+ },
+ {
+ enabled:
+ viewMode === "diff" && !!diffCategory && !!filePath && !!worktreePath,
+ },
+ );
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Only update baseline when content loads
+ useEffect(() => {
+ if (rawFileData?.ok === true && !isDirty) {
+ originalContentRef.current = rawFileData.content;
+ }
+ }, [rawFileData]);
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Only update baseline when diff loads
+ useEffect(() => {
+ if (diffData?.modified && !isDirty) {
+ originalDiffContentRef.current = diffData.modified;
+ }
+ }, [diffData]);
+
+ return {
+ rawFileData,
+ isLoadingRaw,
+ diffData,
+ isLoadingDiff,
+ };
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileSave/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileSave/index.ts
new file mode 100644
index 000000000..215fc763c
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileSave/index.ts
@@ -0,0 +1 @@
+export { useFileSave } from "./useFileSave";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileSave/useFileSave.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileSave/useFileSave.ts
new file mode 100644
index 000000000..074553ac2
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileSave/useFileSave.ts
@@ -0,0 +1,103 @@
+import type * as Monaco from "monaco-editor";
+import { type MutableRefObject, useCallback, useRef } from "react";
+import { trpc } from "renderer/lib/trpc";
+import { useTabsStore } from "renderer/stores/tabs/store";
+import type { ChangeCategory } from "shared/changes-types";
+
+interface UseFileSaveParams {
+ worktreePath: string;
+ filePath: string;
+ paneId: string;
+ diffCategory?: ChangeCategory;
+ editorRef: MutableRefObject;
+ originalContentRef: MutableRefObject;
+ originalDiffContentRef: MutableRefObject;
+ draftContentRef: MutableRefObject;
+ setIsDirty: (dirty: boolean) => void;
+}
+
+export function useFileSave({
+ worktreePath,
+ filePath,
+ paneId,
+ diffCategory,
+ editorRef,
+ originalContentRef,
+ originalDiffContentRef,
+ draftContentRef,
+ setIsDirty,
+}: UseFileSaveParams) {
+ const savingFromRawRef = useRef(false);
+ const savingDiffContentRef = useRef(null);
+ const utils = trpc.useUtils();
+
+ const saveFileMutation = trpc.changes.saveFile.useMutation({
+ onSuccess: () => {
+ setIsDirty(false);
+ if (editorRef.current) {
+ originalContentRef.current = editorRef.current.getValue();
+ }
+ if (savingDiffContentRef.current !== null) {
+ originalDiffContentRef.current = savingDiffContentRef.current;
+ savingDiffContentRef.current = null;
+ }
+ if (savingFromRawRef.current) {
+ draftContentRef.current = null;
+ }
+ savingFromRawRef.current = false;
+
+ utils.changes.readWorkingFile.invalidate();
+ utils.changes.getFileContents.invalidate();
+ utils.changes.getStatus.invalidate();
+
+ if (diffCategory === "staged") {
+ const panes = useTabsStore.getState().panes;
+ const currentPane = panes[paneId];
+ if (currentPane?.fileViewer) {
+ useTabsStore.setState({
+ panes: {
+ ...panes,
+ [paneId]: {
+ ...currentPane,
+ fileViewer: {
+ ...currentPane.fileViewer,
+ diffCategory: "unstaged",
+ },
+ },
+ },
+ });
+ }
+ }
+ },
+ });
+
+ const handleSaveRaw = useCallback(async () => {
+ if (!editorRef.current || !filePath || !worktreePath) return;
+ savingFromRawRef.current = true;
+ await saveFileMutation.mutateAsync({
+ worktreePath,
+ filePath,
+ content: editorRef.current.getValue(),
+ });
+ }, [worktreePath, filePath, saveFileMutation, editorRef]);
+
+ const handleSaveDiff = useCallback(
+ async (content: string) => {
+ if (!filePath || !worktreePath) return;
+ savingFromRawRef.current = false;
+ savingDiffContentRef.current = content;
+ await saveFileMutation.mutateAsync({
+ worktreePath,
+ filePath,
+ content,
+ });
+ },
+ [worktreePath, filePath, saveFileMutation],
+ );
+
+ return {
+ handleSaveRaw,
+ handleSaveDiff,
+ isSaving: saveFileMutation.isPending,
+ };
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/index.ts
new file mode 100644
index 000000000..96c33fa0b
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/index.ts
@@ -0,0 +1 @@
+export { FileViewerPane } from "./FileViewerPane";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
index 6e09a646d..a72f6e168 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
@@ -1,10 +1,5 @@
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
-import { useEffect, useRef, useState } from "react";
-import { HiMiniXMark } from "react-icons/hi2";
-import { TbLayoutColumns, TbLayoutRows } from "react-icons/tb";
+import { useEffect, useRef } from "react";
import type { MosaicBranch } from "react-mosaic-component";
-import { MosaicWindow } from "react-mosaic-component";
-import { HotkeyTooltipContent } from "renderer/components/HotkeyTooltipContent";
import {
registerPaneRef,
unregisterPaneRef,
@@ -14,8 +9,7 @@ import type { Pane, Tab } from "renderer/stores/tabs/types";
import { TabContentContextMenu } from "../TabContentContextMenu";
import { Terminal } from "../Terminal";
import { DirectoryNavigator } from "../Terminal/DirectoryNavigator";
-
-type SplitOrientation = "vertical" | "horizontal";
+import { BasePaneWindow, PaneToolbarActions } from "./components";
interface TabPaneProps {
paneId: string;
@@ -63,12 +57,11 @@ export function TabPane({
onMoveToTab,
onMoveToNewTab,
}: TabPaneProps) {
- const containerRef = useRef(null);
- const [splitOrientation, setSplitOrientation] =
- useState("vertical");
+ const terminalContainerRef = useRef(null);
+ const getClearCallback = useTerminalCallbacksStore((s) => s.getClearCallback);
useEffect(() => {
- const container = containerRef.current;
+ const container = terminalContainerRef.current;
if (container) {
registerPaneRef(paneId, container);
}
@@ -77,61 +70,21 @@ export function TabPane({
};
}, [paneId]);
- useEffect(() => {
- const container = containerRef.current;
- if (!container) return;
-
- const updateOrientation = () => {
- const { width, height } = container.getBoundingClientRect();
- setSplitOrientation(width >= height ? "vertical" : "horizontal");
- };
-
- updateOrientation();
-
- const resizeObserver = new ResizeObserver(updateOrientation);
- resizeObserver.observe(container);
-
- return () => {
- resizeObserver.disconnect();
- };
- }, []);
-
- const handleFocus = () => {
- setFocusedPane(tabId, paneId);
- };
-
- const handleClosePane = (e: React.MouseEvent) => {
- e.stopPropagation();
- removePane(paneId);
- };
-
- const handleSplitPane = (e: React.MouseEvent) => {
- e.stopPropagation();
- const container = containerRef.current;
- if (!container) return;
-
- const { width, height } = container.getBoundingClientRect();
- splitPaneAuto(tabId, paneId, { width, height }, path);
- };
-
- const getClearCallback = useTerminalCallbacksStore((s) => s.getClearCallback);
const handleClearTerminal = () => {
getClearCallback(paneId)?.();
};
- const splitIcon =
- splitOrientation === "vertical" ? (
-
- ) : (
-
- );
-
return (
-
+ (
-
+ tabId={tabId}
+ isActive={isActive}
+ splitPaneAuto={splitPaneAuto}
+ removePane={removePane}
+ setFocusedPane={setFocusedPane}
+ renderToolbar={(handlers) => (
+
-
-
-
-
- {splitIcon}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
)}
- className={isActive ? "mosaic-window-focused" : ""}
>
splitPaneHorizontal(tabId, paneId, path)}
@@ -189,15 +111,10 @@ export function TabPane({
onMoveToTab={onMoveToTab}
onMoveToNewTab={onMoveToNewTab}
>
- {/* biome-ignore lint/a11y/useKeyWithClickEvents lint/a11y/noStaticElementInteractions: Terminal handles its own keyboard events and focus */}
-
+
-
+
);
}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/BasePaneWindow/BasePaneWindow.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/BasePaneWindow/BasePaneWindow.tsx
new file mode 100644
index 000000000..7e7c3d0b2
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/BasePaneWindow/BasePaneWindow.tsx
@@ -0,0 +1,89 @@
+import { useRef } from "react";
+import type { MosaicBranch } from "react-mosaic-component";
+import { MosaicWindow } from "react-mosaic-component";
+import type { SplitOrientation } from "../../hooks";
+import { useSplitOrientation } from "../../hooks";
+
+export interface PaneHandlers {
+ onFocus: () => void;
+ onClosePane: (e: React.MouseEvent) => void;
+ onSplitPane: (e: React.MouseEvent) => void;
+ splitOrientation: SplitOrientation;
+}
+
+interface BasePaneWindowProps {
+ paneId: string;
+ path: MosaicBranch[];
+ tabId: string;
+ isActive: boolean;
+ splitPaneAuto: (
+ tabId: string,
+ sourcePaneId: string,
+ dimensions: { width: number; height: number },
+ path?: MosaicBranch[],
+ ) => void;
+ removePane: (paneId: string) => void;
+ setFocusedPane: (tabId: string, paneId: string) => void;
+ renderToolbar: (handlers: PaneHandlers) => React.ReactElement;
+ children: React.ReactNode;
+ contentClassName?: string;
+}
+
+export function BasePaneWindow({
+ paneId,
+ path,
+ tabId,
+ isActive,
+ splitPaneAuto,
+ removePane,
+ setFocusedPane,
+ renderToolbar,
+ children,
+ contentClassName = "w-full h-full overflow-hidden",
+}: BasePaneWindowProps) {
+ const containerRef = useRef
(null);
+ const splitOrientation = useSplitOrientation(containerRef);
+
+ const handleFocus = () => {
+ setFocusedPane(tabId, paneId);
+ };
+
+ const handleClosePane = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ removePane(paneId);
+ };
+
+ const handleSplitPane = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ const container = containerRef.current;
+ if (!container) return;
+
+ const { width, height } = container.getBoundingClientRect();
+ splitPaneAuto(tabId, paneId, { width, height }, path);
+ };
+
+ const handlers: PaneHandlers = {
+ onFocus: handleFocus,
+ onClosePane: handleClosePane,
+ onSplitPane: handleSplitPane,
+ splitOrientation,
+ };
+
+ return (
+
+ path={path}
+ title=""
+ renderToolbar={() => renderToolbar(handlers)}
+ className={isActive ? "mosaic-window-focused" : ""}
+ >
+ {/* biome-ignore lint/a11y/useKeyWithClickEvents lint/a11y/noStaticElementInteractions: Focus handler for pane */}
+
+ {children}
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/BasePaneWindow/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/BasePaneWindow/index.ts
new file mode 100644
index 000000000..b93b72278
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/BasePaneWindow/index.ts
@@ -0,0 +1 @@
+export { BasePaneWindow, type PaneHandlers } from "./BasePaneWindow";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/PaneToolbarActions/PaneToolbarActions.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/PaneToolbarActions/PaneToolbarActions.tsx
new file mode 100644
index 000000000..5d15fbd1f
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/PaneToolbarActions/PaneToolbarActions.tsx
@@ -0,0 +1,64 @@
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { HiMiniXMark } from "react-icons/hi2";
+import { TbLayoutColumns, TbLayoutRows } from "react-icons/tb";
+import { HotkeyTooltipContent } from "renderer/components/HotkeyTooltipContent";
+import type { HotkeyId } from "shared/hotkeys";
+import type { SplitOrientation } from "../../hooks";
+
+interface PaneToolbarActionsProps {
+ splitOrientation: SplitOrientation;
+ onSplitPane: (e: React.MouseEvent) => void;
+ onClosePane: (e: React.MouseEvent) => void;
+ leadingActions?: React.ReactNode;
+ /** Hotkey ID to display for the close action. Defaults to CLOSE_PANE. */
+ closeHotkeyId?: HotkeyId;
+}
+
+export function PaneToolbarActions({
+ splitOrientation,
+ onSplitPane,
+ onClosePane,
+ leadingActions,
+ closeHotkeyId = "CLOSE_PANE",
+}: PaneToolbarActionsProps) {
+ const splitIcon =
+ splitOrientation === "vertical" ? (
+
+ ) : (
+
+ );
+
+ return (
+
+ {leadingActions}
+
+
+
+ {splitIcon}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/PaneToolbarActions/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/PaneToolbarActions/index.ts
new file mode 100644
index 000000000..adc90aacc
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/PaneToolbarActions/index.ts
@@ -0,0 +1 @@
+export { PaneToolbarActions } from "./PaneToolbarActions";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/index.ts
new file mode 100644
index 000000000..81c4b584b
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/components/index.ts
@@ -0,0 +1,2 @@
+export { BasePaneWindow, type PaneHandlers } from "./BasePaneWindow";
+export { PaneToolbarActions } from "./PaneToolbarActions";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/index.ts
new file mode 100644
index 000000000..7ade988b9
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/index.ts
@@ -0,0 +1,4 @@
+export {
+ type SplitOrientation,
+ useSplitOrientation,
+} from "./useSplitOrientation";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/useSplitOrientation/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/useSplitOrientation/index.ts
new file mode 100644
index 000000000..7ade988b9
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/useSplitOrientation/index.ts
@@ -0,0 +1,4 @@
+export {
+ type SplitOrientation,
+ useSplitOrientation,
+} from "./useSplitOrientation";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/useSplitOrientation/useSplitOrientation.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/useSplitOrientation/useSplitOrientation.ts
new file mode 100644
index 000000000..a239d6e86
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/hooks/useSplitOrientation/useSplitOrientation.ts
@@ -0,0 +1,31 @@
+import { type RefObject, useEffect, useState } from "react";
+
+export type SplitOrientation = "vertical" | "horizontal";
+
+export function useSplitOrientation(
+ containerRef: RefObject,
+): SplitOrientation {
+ const [splitOrientation, setSplitOrientation] =
+ useState("vertical");
+
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+
+ const updateOrientation = () => {
+ const { width, height } = container.getBoundingClientRect();
+ setSplitOrientation(width >= height ? "vertical" : "horizontal");
+ };
+
+ updateOrientation();
+
+ const resizeObserver = new ResizeObserver(updateOrientation);
+ resizeObserver.observe(container);
+
+ return () => {
+ resizeObserver.disconnect();
+ };
+ }, [containerRef]);
+
+ return splitOrientation;
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx
index b6df3608e..766f486ae 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx
@@ -8,6 +8,7 @@ import {
type MosaicNode,
} from "react-mosaic-component";
import { dragDropManager } from "renderer/lib/dnd";
+import { trpc } from "renderer/lib/trpc";
import { useTabsStore } from "renderer/stores/tabs/store";
import type { Pane, Tab } from "renderer/stores/tabs/types";
import {
@@ -15,6 +16,7 @@ import {
extractPaneIdsFromLayout,
getPaneIdsForTab,
} from "renderer/stores/tabs/utils";
+import { FileViewerPane } from "./FileViewerPane";
import { TabPane } from "./TabPane";
interface TabViewProps {
@@ -35,6 +37,10 @@ export function TabView({ tab, panes }: TabViewProps) {
const movePaneToNewTab = useTabsStore((s) => s.movePaneToNewTab);
const allTabs = useTabsStore((s) => s.tabs);
+ // Get worktree path for file viewer panes
+ const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
+ const worktreePath = activeWorkspace?.worktreePath ?? "";
+
// Get tabs in the same workspace for move targets
const workspaceTabs = allTabs.filter(
(t) => t.workspaceId === tab.workspaceId,
@@ -90,6 +96,31 @@ export function TabView({ tab, panes }: TabViewProps) {
);
}
+ // Route file-viewer panes to FileViewerPane component
+ if (pane.type === "file-viewer") {
+ if (!worktreePath) {
+ return (
+
+ Workspace path unavailable
+
+ );
+ }
+ return (
+
+ );
+ }
+
+ // Default: terminal panes
return (
{
const setTabAutoTitle = useTabsStore((s) => s.setTabAutoTitle);
const updatePaneCwd = useTabsStore((s) => s.updatePaneCwd);
const focusedPaneIds = useTabsStore((s) => s.focusedPaneIds);
+ const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane);
const terminalTheme = useTerminalTheme();
// Ref for initial theme to avoid recreating terminal on theme change
@@ -68,6 +71,76 @@ export const Terminal = ({ tabId, workspaceId }: TerminalProps) => {
const { data: workspaceCwd } =
trpc.terminal.getWorkspaceCwd.useQuery(workspaceId);
+ // Query terminal link behavior setting
+ const { data: terminalLinkBehavior } =
+ trpc.settings.getTerminalLinkBehavior.useQuery();
+
+ // Handler for file link clicks - uses current setting value
+ const handleFileLinkClick = useCallback(
+ (path: string, line?: number, column?: number) => {
+ const behavior = terminalLinkBehavior ?? "external-editor";
+
+ // Helper to open in external editor
+ const openInExternalEditor = () => {
+ trpcClient.external.openFileInEditor
+ .mutate({
+ path,
+ line,
+ column,
+ cwd: workspaceCwd ?? undefined,
+ })
+ .catch((error) => {
+ console.error(
+ "[Terminal] Failed to open file in editor:",
+ path,
+ error,
+ );
+ toast.error("Failed to open file in editor", {
+ description: path,
+ });
+ });
+ };
+
+ if (behavior === "file-viewer") {
+ // If workspaceCwd is not loaded yet, fall back to external editor
+ // This prevents confusing errors when the workspace is still initializing
+ if (!workspaceCwd) {
+ console.warn(
+ "[Terminal] workspaceCwd not loaded, falling back to external editor",
+ );
+ openInExternalEditor();
+ return;
+ }
+
+ // Normalize absolute paths to worktree-relative paths for file viewer
+ // File viewer expects relative paths, but terminal links can be absolute
+ let filePath = path;
+ // Use path boundary check to avoid incorrect prefix stripping
+ // e.g., /repo vs /repo-other should not match
+ if (path === workspaceCwd) {
+ filePath = ".";
+ } else if (path.startsWith(`${workspaceCwd}/`)) {
+ filePath = path.slice(workspaceCwd.length + 1);
+ } else if (path.startsWith("/")) {
+ // Absolute path outside workspace - show warning and don't attempt to open
+ toast.warning("File is outside the workspace", {
+ description:
+ "Switch to 'External editor' in Settings to open this file",
+ });
+ return;
+ }
+ addFileViewerPane(workspaceId, { filePath, line, column });
+ } else {
+ openInExternalEditor();
+ }
+ },
+ [terminalLinkBehavior, workspaceId, workspaceCwd, addFileViewerPane],
+ );
+
+ // Ref to avoid terminal recreation when callback changes
+ const handleFileLinkClickRef = useRef(handleFileLinkClick);
+ handleFileLinkClickRef.current = handleFileLinkClick;
+
// Seed cwd from initialCwd or workspace path (shell spawns there)
// OSC-7 will override if/when the shell reports directory changes
useEffect(() => {
@@ -197,11 +270,12 @@ export const Terminal = ({ tabId, workspaceId }: TerminalProps) => {
xterm,
fitAddon,
cleanup: cleanupQuerySuppression,
- } = createTerminalInstance(
- container,
- workspaceCwd ?? undefined,
- initialThemeRef.current,
- );
+ } = createTerminalInstance(container, {
+ cwd: workspaceCwd,
+ initialTheme: initialThemeRef.current,
+ onFileLinkClick: (path, line, column) =>
+ handleFileLinkClickRef.current(path, line, column),
+ });
xtermRef.current = xterm;
fitAddonRef.current = fitAddon;
isExitedRef.current = false;
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts
index 3c92f907d..fbd1b980b 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts
@@ -93,19 +93,26 @@ function loadRenderer(xterm: XTerm): { dispose: () => void } {
};
}
+export interface CreateTerminalOptions {
+ cwd?: string;
+ initialTheme?: ITheme | null;
+ onFileLinkClick?: (path: string, line?: number, column?: number) => void;
+}
+
export function createTerminalInstance(
container: HTMLDivElement,
- cwd?: string,
- initialTheme?: ITheme | null,
+ options: CreateTerminalOptions = {},
): {
xterm: XTerm;
fitAddon: FitAddon;
cleanup: () => void;
} {
+ const { cwd, initialTheme, onFileLinkClick } = options;
+
// Use provided theme, or fall back to localStorage-based default to prevent flash
const theme = initialTheme ?? getDefaultTerminalTheme();
- const options = { ...TERMINAL_OPTIONS, theme };
- const xterm = new XTerm(options);
+ const terminalOptions = { ...TERMINAL_OPTIONS, theme };
+ const xterm = new XTerm(terminalOptions);
const fitAddon = new FitAddon();
const clipboardAddon = new ClipboardAddon();
@@ -149,20 +156,25 @@ export function createTerminalInstance(
const filePathLinkProvider = new FilePathLinkProvider(
xterm,
(_event, path, line, column) => {
- trpcClient.external.openFileInEditor
- .mutate({
- path,
- line,
- column,
- cwd,
- })
- .catch((error) => {
- console.error(
- "[Terminal] Failed to open file in editor:",
+ if (onFileLinkClick) {
+ onFileLinkClick(path, line, column);
+ } else {
+ // Fallback to default behavior (external editor)
+ trpcClient.external.openFileInEditor
+ .mutate({
path,
- error,
- );
- });
+ line,
+ column,
+ cwd,
+ })
+ .catch((error) => {
+ console.error(
+ "[Terminal] Failed to open file in editor:",
+ path,
+ error,
+ );
+ });
+ }
},
);
xterm.registerLinkProvider(filePathLinkProvider);
@@ -233,7 +245,7 @@ export function setupPasteHandler(
/**
* Setup keyboard handling for xterm including:
- * - Shortcut forwarding: App hotkeys are re-dispatched to document for react-hotkeys-hook
+ * - Shortcut forwarding: App hotkeys bubble to document where useAppHotkey listens
* - Shift+Enter: Sends ESC+CR sequence (to avoid \ appearing in Claude Code while keeping line continuation behavior)
* - Clear terminal: Uses the configured clear shortcut
*
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
index e04866eb6..b62872350 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
@@ -1,6 +1,7 @@
import { useMemo } from "react";
import { trpc } from "renderer/lib/trpc";
import { useTabsStore } from "renderer/stores/tabs/store";
+import { ResizableSidebar } from "../../../WorkspaceView/ResizableSidebar";
import { EmptyTabView } from "./EmptyTabView";
import { TabView } from "./TabView";
@@ -19,9 +20,16 @@ export function TabsContent() {
return allTabs.find((tab) => tab.id === activeTabId) || null;
}, [activeWorkspaceId, activeTabIds, allTabs]);
- if (!tabToRender) {
- return ;
- }
-
- return ;
+ return (
+
+
+ {tabToRender ? (
+
+ ) : (
+
+ )}
+
+
+
+ );
}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
index 1a2e20bd0..ecc345fae 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
@@ -1,19 +1,15 @@
-import { SidebarMode, useSidebarStore } from "renderer/stores";
-import { ChangesContent } from "./ChangesContent";
+import { SidebarControl } from "../../SidebarControl";
+import { ContentHeader } from "./ContentHeader";
import { TabsContent } from "./TabsContent";
+import { GroupStrip } from "./TabsContent/GroupStrip";
export function ContentView() {
- const { currentMode } = useSidebarStore();
-
- if (currentMode === SidebarMode.Changes) {
- return (
-
- );
- }
-
- return ;
+ return (
+
+ }>
+
+
+
+
+ );
}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ResizableSidebar/ResizableSidebar.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ResizableSidebar/ResizableSidebar.tsx
index 05d9d7b04..f6a00b367 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ResizableSidebar/ResizableSidebar.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ResizableSidebar/ResizableSidebar.tsx
@@ -33,8 +33,8 @@ export function ResizableSidebar() {
(e: MouseEvent) => {
if (!isResizing) return;
- const delta = e.clientX - startXRef.current;
- const newWidth = startWidthRef.current + delta;
+ const draggedLeftBy = startXRef.current - e.clientX;
+ const newWidth = startWidthRef.current + draggedLeftBy;
const clampedWidth = Math.max(
MIN_SIDEBAR_WIDTH,
Math.min(MAX_SIDEBAR_WIDTH, newWidth),
@@ -87,8 +87,8 @@ export function ResizableSidebar() {
tabIndex={0}
onMouseDown={handleMouseDown}
className={cn(
- "absolute top-0 -right-2 w-5 h-full cursor-col-resize z-10",
- "after:absolute after:top-0 after:left-2 after:w-1 after:h-full after:transition-colors",
+ "absolute top-0 -left-2 w-5 h-full cursor-col-resize z-10",
+ "after:absolute after:top-0 after:right-2 after:w-1 after:h-full after:transition-colors",
"hover:after:bg-border focus:outline-none focus:after:bg-border",
isResizing && "after:bg-border",
)}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsx
index ee0ec6d2e..2ba11f811 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsx
@@ -6,13 +6,22 @@ import { HiMiniMinus, HiMiniPlus } from "react-icons/hi2";
import { trpc } from "renderer/lib/trpc";
import { useChangesStore } from "renderer/stores/changes";
import type { ChangeCategory, ChangedFile } from "shared/changes-types";
+
import { CategorySection } from "./components/CategorySection";
import { ChangesHeader } from "./components/ChangesHeader";
import { CommitInput } from "./components/CommitInput";
import { CommitItem } from "./components/CommitItem";
import { FileList } from "./components/FileList";
-export function ChangesView() {
+interface ChangesViewProps {
+ onFileOpen?: (
+ file: ChangedFile,
+ category: ChangeCategory,
+ commitHash?: string,
+ ) => void;
+}
+
+export function ChangesView({ onFileOpen }: ChangesViewProps) {
const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
const worktreePath = activeWorkspace?.worktreePath;
@@ -128,11 +137,13 @@ export function ChangesView() {
const handleFileSelect = (file: ChangedFile, category: ChangeCategory) => {
if (!worktreePath) return;
selectFile(worktreePath, file, category, null);
+ onFileOpen?.(file, category);
};
const handleCommitFileSelect = (file: ChangedFile, commitHash: string) => {
if (!worktreePath) return;
selectFile(worktreePath, file, "committed", commitHash);
+ onFileOpen?.(file, "committed", commitHash);
};
const handleCommitToggle = (hash: string) => {
@@ -206,6 +217,7 @@ export function ChangesView() {
viewMode={fileListViewMode}
onViewModeChange={setFileListViewMode}
worktreePath={worktreePath}
+ workspaceId={activeWorkspace?.id}
/>
- {availableBranches.map((branch) => (
-
- {branch}
- {branch === branchData.defaultBranch && (
- (default)
- )}
-
- ))}
+ {availableBranches
+ .filter((branch) => branch)
+ .map((branch) => (
+
+ {branch}
+ {branch === branchData.defaultBranch && (
+ (default)
+ )}
+
+ ))}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx
index 0c93c0153..e1e2b8421 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx
@@ -8,7 +8,10 @@ import {
} from "@superset/ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import { HiArrowPath } from "react-icons/hi2";
+import { LuLoaderCircle } from "react-icons/lu";
import { trpc } from "renderer/lib/trpc";
+import { PRIcon } from "renderer/screens/main/components/PRIcon";
+import { usePRStatus } from "renderer/screens/main/hooks";
import { useChangesStore } from "renderer/stores/changes";
import type { ChangesViewMode } from "../../types";
import { ViewModeToggle } from "../ViewModeToggle";
@@ -21,6 +24,7 @@ interface ChangesHeaderProps {
viewMode: ChangesViewMode;
onViewModeChange: (mode: ChangesViewMode) => void;
worktreePath: string;
+ workspaceId?: string;
}
export function ChangesHeader({
@@ -31,6 +35,7 @@ export function ChangesHeader({
viewMode,
onViewModeChange,
worktreePath,
+ workspaceId,
}: ChangesHeaderProps) {
const { baseBranch, setBaseBranch } = useChangesStore();
@@ -39,6 +44,11 @@ export function ChangesHeader({
{ enabled: !!worktreePath },
);
+ const { pr, isLoading: isPRLoading } = usePRStatus({
+ workspaceId,
+ refetchInterval: 10000,
+ });
+
const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? "main";
const availableBranches = branchData?.remote ?? [];
@@ -74,16 +84,18 @@ export function ChangesHeader({
- {sortedBranches.map((branch) => (
-
- {branch}
- {branch === branchData.defaultBranch && (
-
- (default)
-
- )}
-
- ))}
+ {sortedBranches
+ .filter((branch) => branch)
+ .map((branch) => (
+
+ {branch}
+ {branch === branchData.defaultBranch && (
+
+ (default)
+
+ )}
+
+ ))}
@@ -113,6 +125,30 @@ export function ChangesHeader({
Refresh changes
+
+ {/* PR Status Icon */}
+ {isPRLoading ? (
+
+ ) : pr ? (
+
+
+
+
+
+ #{pr.number}
+
+
+
+
+ View PR on GitHub
+
+
+ ) : null}
);
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/PresetContextMenu/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/PresetContextMenu/index.tsx
deleted file mode 100644
index a9b7009d4..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/PresetContextMenu/index.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import {
- ContextMenu,
- ContextMenuContent,
- ContextMenuItem,
- ContextMenuSeparator,
- ContextMenuTrigger,
-} from "@superset/ui/context-menu";
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
-import type React from "react";
-
-interface PresetContextMenuProps {
- hasActiveTab: boolean;
- tooltipText?: string;
- onOpenAsNewTab: () => void;
- onOpenAsPane: () => void;
- children: React.ReactNode;
-}
-
-export function PresetContextMenu({
- hasActiveTab,
- tooltipText,
- onOpenAsNewTab,
- onOpenAsPane,
- children,
-}: PresetContextMenuProps) {
- const contextMenuContent = (
-
-
- Open as New Tab
-
- {hasActiveTab && (
- <>
-
-
- Open as Pane in Current Tab
-
- >
- )}
-
- );
-
- if (!tooltipText) {
- return (
-
- {children}
- {contextMenuContent}
-
- );
- }
-
- return (
-
-
-
- {children}
-
- {contextMenuContent}
-
- {tooltipText}
-
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabItem/TabContextMenu.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabItem/TabContextMenu.tsx
deleted file mode 100644
index 0ce5492de..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabItem/TabContextMenu.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import {
- ContextMenu,
- ContextMenuContent,
- ContextMenuItem,
- ContextMenuSeparator,
- ContextMenuTrigger,
-} from "@superset/ui/context-menu";
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
-import type React from "react";
-
-interface TabContextMenuProps {
- paneCount: number;
- onClose: () => void;
- onRename: () => void;
- children: React.ReactNode;
-}
-
-export function TabContextMenu({
- paneCount,
- onClose,
- onRename,
- children,
-}: TabContextMenuProps) {
- const hasMultiplePanes = paneCount > 1;
-
- const handleRenameSelect = (event: Event) => {
- // Prevent default to stop Radix from restoring focus to the trigger
- event.preventDefault();
- onRename();
- };
-
- const contextMenuContent = (
-
- {children}
-
-
- Rename Tab
-
-
-
- Close Tab
-
-
-
- );
-
- if (!hasMultiplePanes) {
- return contextMenuContent;
- }
-
- return (
-
-
-
- {children}
-
-
-
- Rename Tab
-
-
-
- Close Tab
-
-
-
-
- {paneCount} terminals
-
-
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabItem/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabItem/index.tsx
deleted file mode 100644
index 07a860bed..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabItem/index.tsx
+++ /dev/null
@@ -1,218 +0,0 @@
-import { Button } from "@superset/ui/button";
-import { Input } from "@superset/ui/input";
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
-import { useEffect, useMemo, useRef, useState } from "react";
-import { useDrag, useDrop } from "react-dnd";
-import { HiMiniCommandLine, HiMiniXMark } from "react-icons/hi2";
-import { trpc } from "renderer/lib/trpc";
-import { useTabsStore } from "renderer/stores/tabs/store";
-import type { Tab } from "renderer/stores/tabs/types";
-import { getTabDisplayName } from "renderer/stores/tabs/utils";
-import { TabContextMenu } from "./TabContextMenu";
-
-const DRAG_TYPE = "TAB";
-
-interface DragItem {
- type: typeof DRAG_TYPE;
- tabId: string;
- index: number;
-}
-
-interface TabItemProps {
- tab: Tab;
- index: number;
- isActive: boolean;
-}
-
-export function TabItem({ tab, index, isActive }: TabItemProps) {
- const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
- const activeWorkspaceId = activeWorkspace?.id;
- const removeTab = useTabsStore((s) => s.removeTab);
- const setActiveTab = useTabsStore((s) => s.setActiveTab);
- const renameTab = useTabsStore((s) => s.renameTab);
- const panes = useTabsStore((s) => s.panes);
- const needsAttention = useTabsStore((s) =>
- Object.values(s.panes).some((p) => p.tabId === tab.id && p.needsAttention),
- );
-
- const paneCount = useMemo(
- () => Object.values(panes).filter((p) => p.tabId === tab.id).length,
- [panes, tab.id],
- );
-
- const [isRenaming, setIsRenaming] = useState(false);
- const [renameValue, setRenameValue] = useState("");
- const inputRef = useRef
(null);
-
- // Drag source for tab reordering
- const [{ isDragging }, drag] = useDrag<
- DragItem,
- void,
- { isDragging: boolean }
- >({
- type: DRAG_TYPE,
- item: { type: DRAG_TYPE, tabId: tab.id, index },
- collect: (monitor) => ({
- isDragging: monitor.isDragging(),
- }),
- });
-
- // Drop target (just for visual feedback, actual drop is handled by parent)
- const [{ isDragOver }, drop] = useDrop<
- DragItem,
- void,
- { isDragOver: boolean }
- >({
- accept: DRAG_TYPE,
- collect: (monitor) => ({
- isDragOver: monitor.isOver(),
- }),
- });
-
- const displayName = getTabDisplayName(tab);
-
- const handleRemoveTab = (e?: React.MouseEvent) => {
- e?.stopPropagation();
- removeTab(tab.id);
- };
-
- const handleTabClick = () => {
- if (isRenaming) return;
- if (activeWorkspaceId) {
- setActiveTab(activeWorkspaceId, tab.id);
- }
- };
-
- const startRename = () => {
- setRenameValue(tab.userTitle ?? tab.name ?? displayName);
- setIsRenaming(true);
- };
-
- // Focus input when entering rename mode
- useEffect(() => {
- if (isRenaming && inputRef.current) {
- inputRef.current.focus();
- inputRef.current.select();
- }
- }, [isRenaming]);
-
- const submitRename = () => {
- const trimmedValue = renameValue.trim();
- const currentUserTitle = tab.userTitle?.trim() ?? "";
- if (trimmedValue !== currentUserTitle) {
- renameTab(tab.id, trimmedValue);
- }
- setIsRenaming(false);
- };
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if (e.key === "Enter") {
- e.preventDefault();
- submitRename();
- } else if (e.key === "Escape") {
- setIsRenaming(false);
- }
- };
-
- const attachRef = (el: HTMLElement | null) => {
- drag(el);
- drop(el);
- };
-
- // When renaming, render outside TabContextMenu to avoid Radix focus interference
- if (isRenaming) {
- return (
-
-
-
-
-
- setRenameValue(e.target.value)}
- onBlur={submitRename}
- onKeyDown={handleKeyDown}
- onClick={(e) => e.stopPropagation()}
- className="flex-1"
- />
-
-
-
-
-
-
-
- );
- }
-
- return (
-
-
-
-
{
- if (e.key === "Enter" || e.key === " ") {
- e.preventDefault();
- handleTabClick();
- }
- }}
- tabIndex={0}
- className={`
- w-full text-start px-3 py-2 rounded-md cursor-pointer flex items-center justify-between pr-8
- ${isActive ? "bg-tertiary-active" : ""}
- ${isDragging ? "opacity-50" : ""}
- ${isDragOver ? "bg-tertiary-active/50" : ""}
- `}
- >
-
-
- {displayName}
- {needsAttention && (
-
-
-
-
-
-
-
- Agent completed
-
- )}
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabsCommandDialog/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabsCommandDialog/index.tsx
deleted file mode 100644
index 63ee4320e..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/TabsCommandDialog/index.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import type { TerminalPreset } from "@superset/local-db";
-import {
- CommandDialog,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@superset/ui/command";
-import {
- HiMiniCommandLine,
- HiMiniPlus,
- HiOutlineCog6Tooth,
-} from "react-icons/hi2";
-import {
- getPresetIcon,
- useIsDarkTheme,
-} from "renderer/assets/app-icons/preset-icons";
-
-interface TabsCommandDialogProps {
- open: boolean;
- onOpenChange: (open: boolean) => void;
- onAddTab: () => void;
- onOpenPresetsSettings: () => void;
- presets: TerminalPreset[];
- onSelectPreset: (preset: TerminalPreset) => void;
-}
-
-export function TabsCommandDialog({
- open,
- onOpenChange,
- onAddTab,
- onOpenPresetsSettings,
- presets,
- onSelectPreset,
-}: TabsCommandDialogProps) {
- const isDark = useIsDarkTheme();
-
- return (
-
-
-
- No results found.
-
-
-
- New Terminal
-
-
- {presets.length > 0 && (
-
- {presets.map((preset) => {
- const presetIcon = getPresetIcon(preset.name, isDark);
- return (
- onSelectPreset(preset)}
- >
- {presetIcon ? (
-
- ) : (
-
- )}
-
- {preset.name || "default"}
-
- {preset.description ? (
-
- {preset.description}
-
- ) : (
- preset.cwd && (
-
- {preset.cwd}
-
- )
- )}
-
- );
- })}
-
- )}
-
-
-
- Configure Presets
-
-
-
-
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/index.tsx
deleted file mode 100644
index 2baa29305..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/TabsView/index.tsx
+++ /dev/null
@@ -1,284 +0,0 @@
-import type { TerminalPreset } from "@superset/local-db";
-import { Button } from "@superset/ui/button";
-import { ButtonGroup } from "@superset/ui/button-group";
-import { LayoutGroup, motion } from "framer-motion";
-import { useMemo, useRef, useState } from "react";
-import { useDrop } from "react-dnd";
-import {
- HiMiniCommandLine,
- HiMiniEllipsisHorizontal,
- HiMiniPlus,
-} from "react-icons/hi2";
-import {
- getPresetIcon,
- useIsDarkTheme,
-} from "renderer/assets/app-icons/preset-icons";
-import { trpc } from "renderer/lib/trpc";
-import { usePresets } from "renderer/react-query/presets";
-import { useOpenSettings, useSidebarStore } from "renderer/stores";
-import { useTabsStore } from "renderer/stores/tabs/store";
-import { PortsList } from "./PortsList";
-import { PresetContextMenu } from "./PresetContextMenu";
-import { TabItem } from "./TabItem";
-import { TabsCommandDialog } from "./TabsCommandDialog";
-
-const DRAG_TYPE = "TAB";
-
-interface DragItem {
- type: typeof DRAG_TYPE;
- tabId: string;
- index: number;
-}
-
-export function TabsView() {
- const isResizing = useSidebarStore((s) => s.isResizing);
- const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
- const activeWorkspaceId = activeWorkspace?.id;
- const allTabs = useTabsStore((s) => s.tabs);
- const addTab = useTabsStore((s) => s.addTab);
- const addPane = useTabsStore((s) => s.addPane);
- const renameTab = useTabsStore((s) => s.renameTab);
- const reorderTabById = useTabsStore((s) => s.reorderTabById);
- const activeTabIds = useTabsStore((s) => s.activeTabIds);
- const [dropIndex, setDropIndex] = useState(null);
- const [commandOpen, setCommandOpen] = useState(false);
- const openSettings = useOpenSettings();
- const containerRef = useRef(null);
-
- const { presets } = usePresets();
- const isDark = useIsDarkTheme();
-
- const tabs = useMemo(
- () =>
- activeWorkspaceId
- ? allTabs.filter((tab) => tab.workspaceId === activeWorkspaceId)
- : [],
- [activeWorkspaceId, allTabs],
- );
-
- const handleAddTab = () => {
- if (activeWorkspaceId) {
- addTab(activeWorkspaceId);
- setCommandOpen(false);
- }
- };
-
- const handleAddPane = () => {
- if (!activeWorkspaceId) return;
-
- const activeTabId = activeTabIds[activeWorkspaceId];
- if (!activeTabId) {
- // Fall back to creating a new tab if no active tab
- handleAddTab();
- return;
- }
-
- addPane(activeTabId);
- setCommandOpen(false);
- };
-
- const handleOpenPresetsSettings = () => {
- openSettings("presets");
- setCommandOpen(false);
- };
-
- const handleSelectPreset = (preset: TerminalPreset) => {
- if (!activeWorkspaceId) return;
-
- // Pass preset options to addTab - Terminal component will read them from pane state
- const { tabId } = addTab(activeWorkspaceId, {
- initialCommands: preset.commands,
- initialCwd: preset.cwd || undefined,
- });
-
- // Rename the tab to the preset name
- if (preset.name) {
- renameTab(tabId, preset.name);
- }
-
- setCommandOpen(false);
- };
-
- const handleSelectPresetAsPane = (preset: TerminalPreset) => {
- if (!activeWorkspaceId) return;
-
- const activeTabId = activeTabIds[activeWorkspaceId];
- if (!activeTabId) {
- // Fall back to opening as new tab if no active tab
- handleSelectPreset(preset);
- return;
- }
-
- // Add pane to current tab with preset options
- addPane(activeTabId, {
- initialCommands: preset.commands,
- initialCwd: preset.cwd || undefined,
- });
-
- setCommandOpen(false);
- };
-
- const [{ isOver }, drop] = useDrop({
- accept: DRAG_TYPE,
- hover: (item, monitor) => {
- if (!containerRef.current) return;
-
- const clientOffset = monitor.getClientOffset();
- if (!clientOffset) return;
-
- const tabItems = containerRef.current.querySelectorAll("[data-tab-item]");
- let newDropIndex = tabs.length;
-
- tabItems.forEach((element, index) => {
- const rect = element.getBoundingClientRect();
- const midY = rect.top + rect.height / 2;
-
- if (clientOffset.y < midY && index < newDropIndex) {
- newDropIndex = index;
- }
- });
-
- if (newDropIndex === item.index || newDropIndex === item.index + 1) {
- setDropIndex(null);
- } else {
- setDropIndex(newDropIndex);
- }
- },
- drop: (item) => {
- if (dropIndex !== null && dropIndex !== item.index) {
- const targetIndex = dropIndex > item.index ? dropIndex - 1 : dropIndex;
- reorderTabById(item.tabId, targetIndex);
- }
- setDropIndex(null);
- },
- collect: (monitor) => ({
- isOver: monitor.isOver(),
- }),
- });
-
- if (!isOver && dropIndex !== null) {
- setDropIndex(null);
- }
-
- return (
-
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
index 751158761..86b5df975 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
@@ -1,29 +1,28 @@
-import { useSidebarStore } from "renderer/stores";
-import { SidebarMode } from "renderer/stores/sidebar-state";
+import { trpc } from "renderer/lib/trpc";
+import { useTabsStore } from "renderer/stores/tabs/store";
+import type { ChangeCategory, ChangedFile } from "shared/changes-types";
import { ChangesView } from "./ChangesView";
-import { ModeCarousel } from "./ModeCarousel";
-import { TabsView } from "./TabsView";
export function Sidebar() {
- const { currentMode, setMode } = useSidebarStore();
+ const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
+ const workspaceId = activeWorkspace?.id;
- const modes: SidebarMode[] = [SidebarMode.Tabs, SidebarMode.Changes];
+ const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane);
+
+ const handleFileOpen = workspaceId
+ ? (file: ChangedFile, category: ChangeCategory, commitHash?: string) => {
+ addFileViewerPane(workspaceId, {
+ filePath: file.path,
+ diffCategory: category,
+ commitHash,
+ oldPath: file.oldPath,
+ });
+ }
+ : undefined;
return (
);
}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/WorkspaceActionBar.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/WorkspaceActionBar.tsx
deleted file mode 100644
index f641fde5a..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/WorkspaceActionBar.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { WorkspaceActionBarLeft } from "./components/WorkspaceActionBarLeft";
-import { WorkspaceActionBarRight } from "./components/WorkspaceActionBarRight";
-
-interface WorkspaceActionBarProps {
- worktreePath: string | undefined;
-}
-
-export function WorkspaceActionBar({ worktreePath }: WorkspaceActionBarProps) {
- if (!worktreePath) return null;
-
- return (
-
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarLeft/WorkspaceActionBarLeft.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarLeft/WorkspaceActionBarLeft.tsx
deleted file mode 100644
index 0db773eb9..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarLeft/WorkspaceActionBarLeft.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
-import { GoGitBranch } from "react-icons/go";
-import { trpc } from "renderer/lib/trpc";
-
-export function WorkspaceActionBarLeft() {
- const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
- const currentBranch = activeWorkspace?.worktree?.branch;
- const baseBranch = activeWorkspace?.worktree?.baseBranch;
- return (
- <>
- {currentBranch && (
-
-
-
-
-
- {currentBranch}
-
-
-
-
- Current branch
-
-
- )}
- {baseBranch && baseBranch !== currentBranch && (
-
-
-
- from
- {baseBranch}
-
-
-
- Based on {baseBranch}
-
-
- )}
- >
- );
-}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarLeft/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarLeft/index.ts
deleted file mode 100644
index 9a42acaa8..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarLeft/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { WorkspaceActionBarLeft } from "./WorkspaceActionBarLeft";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/index.ts
deleted file mode 100644
index da70bada5..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { WorkspaceActionBarRight } from "./WorkspaceActionBarRight";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/index.ts
deleted file mode 100644
index c8caa3fbc..000000000
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { WorkspaceActionBar } from "./WorkspaceActionBar";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceInitializingView/WorkspaceInitializingView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceInitializingView/WorkspaceInitializingView.tsx
new file mode 100644
index 000000000..3140858c2
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceInitializingView/WorkspaceInitializingView.tsx
@@ -0,0 +1,328 @@
+import {
+ AlertDialog,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@superset/ui/alert-dialog";
+import { Button } from "@superset/ui/button";
+import { cn } from "@superset/ui/utils";
+import { useState } from "react";
+import { HiExclamationTriangle } from "react-icons/hi2";
+import { LuCheck, LuCircle, LuGitBranch, LuLoader } from "react-icons/lu";
+import { trpc } from "renderer/lib/trpc";
+import {
+ useHasWorkspaceFailed,
+ useWorkspaceInitProgress,
+} from "renderer/stores/workspace-init";
+import {
+ INIT_STEP_MESSAGES,
+ INIT_STEP_ORDER,
+ isStepComplete,
+ type WorkspaceInitStep,
+} from "shared/types/workspace-init";
+
+interface WorkspaceInitializingViewProps {
+ workspaceId: string;
+ workspaceName: string;
+ /** True if init was interrupted (e.g., app restart during init) */
+ isInterrupted?: boolean;
+}
+
+// Steps to display in the progress view (skip pending and ready)
+const DISPLAY_STEPS: WorkspaceInitStep[] = INIT_STEP_ORDER.filter(
+ (step) => step !== "pending" && step !== "ready",
+);
+
+export function WorkspaceInitializingView({
+ workspaceId,
+ workspaceName,
+ isInterrupted = false,
+}: WorkspaceInitializingViewProps) {
+ const progress = useWorkspaceInitProgress(workspaceId);
+ const hasFailed = useHasWorkspaceFailed(workspaceId);
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
+
+ const retryMutation = trpc.workspaces.retryInit.useMutation();
+ const deleteMutation = trpc.workspaces.delete.useMutation();
+ const utils = trpc.useUtils();
+
+ const handleRetry = () => {
+ retryMutation.mutate(
+ { workspaceId },
+ {
+ onSuccess: () => {
+ utils.workspaces.invalidate();
+ },
+ },
+ );
+ };
+
+ const handleDelete = () => {
+ setShowDeleteConfirm(false);
+ deleteMutation.mutate(
+ { id: workspaceId },
+ {
+ onSuccess: () => {
+ utils.workspaces.invalidate();
+ },
+ },
+ );
+ };
+
+ const currentStep = progress?.step ?? "pending";
+
+ // Interrupted state (app restart during init - no in-memory progress)
+ if (isInterrupted && !progress) {
+ return (
+ <>
+
+
+ {/* Warning icon */}
+
+
+
+
+ {/* Title and description */}
+
+
+ Setup was interrupted
+
+
{workspaceName}
+
+ The app was closed before workspace setup completed. You can
+ retry the setup or delete this workspace.
+
+
+
+ {/* Action buttons */}
+
+ setShowDeleteConfirm(true)}
+ disabled={deleteMutation.isPending}
+ >
+ {deleteMutation.isPending ? "Deleting..." : "Delete Workspace"}
+
+
+ {retryMutation.isPending ? (
+ <>
+
+ Retrying...
+ >
+ ) : (
+ "Retry Setup"
+ )}
+
+
+
+
+
+ {/* Delete confirmation dialog */}
+
+
+
+
+ Delete workspace "{workspaceName}"?
+
+
+
+ This workspace was not fully set up. Deleting will clean up
+ any partial files that were created.
+
+
+
+
+ setShowDeleteConfirm(false)}
+ >
+ Cancel
+
+
+ Delete
+
+
+
+
+ >
+ );
+ }
+
+ // Failed state
+ if (hasFailed) {
+ return (
+ <>
+
+
+ {/* Error icon */}
+
+
+
+
+ {/* Title and description */}
+
+
+ Workspace setup failed
+
+
{workspaceName}
+ {progress?.error && (
+
+ {progress.error}
+
+ )}
+
+
+ {/* Action buttons */}
+
+ setShowDeleteConfirm(true)}
+ disabled={deleteMutation.isPending}
+ >
+ {deleteMutation.isPending ? "Deleting..." : "Delete Workspace"}
+
+
+ {retryMutation.isPending ? (
+ <>
+
+ Retrying...
+ >
+ ) : (
+ "Retry"
+ )}
+
+
+
+
+
+ {/* Delete confirmation dialog */}
+
+
+
+
+ Delete workspace "{workspaceName}"?
+
+
+
+ This workspace failed to initialize. Deleting will clean up
+ any partial files that were created.
+
+
+
+
+ setShowDeleteConfirm(false)}
+ >
+ Cancel
+
+
+ Delete
+
+
+
+
+ >
+ );
+ }
+
+ // Initializing state
+ return (
+
+
+ {/* Icon with pulse animation */}
+
+
+ {/* Title and description */}
+
+
+ Setting up workspace
+
+
{workspaceName}
+
+
+ {/* Step list */}
+
+ {DISPLAY_STEPS.map((step) => {
+ const isComplete = isStepComplete(step, currentStep);
+ const isCurrent = step === currentStep;
+
+ return (
+
+ {/* Step icon */}
+ {isComplete ? (
+
+ ) : isCurrent ? (
+
+ ) : (
+
+ )}
+
+ {/* Step label */}
+
+ {INIT_STEP_MESSAGES[step]}
+
+
+ );
+ })}
+
+
+ {/* Helper text */}
+
+ Takes 10s to a few minutes depending on the size of your repo
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceInitializingView/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceInitializingView/index.ts
new file mode 100644
index 000000000..6685f11c9
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceInitializingView/index.ts
@@ -0,0 +1 @@
+export { WorkspaceInitializingView } from "./WorkspaceInitializingView";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/index.tsx
index 661543c85..ac24a87d6 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/index.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/index.tsx
@@ -3,13 +3,33 @@ import { trpc } from "renderer/lib/trpc";
import { useAppHotkey } from "renderer/stores/hotkeys";
import { useTabsStore } from "renderer/stores/tabs/store";
import { getNextPaneId, getPreviousPaneId } from "renderer/stores/tabs/utils";
+import {
+ useHasWorkspaceFailed,
+ useIsWorkspaceInitializing,
+} from "renderer/stores/workspace-init";
import { ContentView } from "./ContentView";
-import { ResizableSidebar } from "./ResizableSidebar";
-import { WorkspaceActionBar } from "./WorkspaceActionBar";
+import { WorkspaceInitializingView } from "./WorkspaceInitializingView";
export function WorkspaceView() {
const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
const activeWorkspaceId = activeWorkspace?.id;
+
+ // Check if active workspace is initializing or failed
+ const isInitializing = useIsWorkspaceInitializing(activeWorkspaceId ?? "");
+ const hasFailed = useHasWorkspaceFailed(activeWorkspaceId ?? "");
+
+ // Also check for incomplete init after app restart:
+ // - worktree type workspace with null/undefined gitStatus means init never completed
+ // - This handles the case where app restarts during init (in-memory progress lost)
+ // - Uses explicit check instead of == null to avoid lint issues
+ const gitStatus = activeWorkspace?.worktree?.gitStatus;
+ const hasIncompleteInit =
+ activeWorkspace?.type === "worktree" &&
+ (gitStatus === null || gitStatus === undefined);
+
+ const showInitView =
+ activeWorkspaceId && (isInitializing || hasFailed || hasIncompleteInit);
+
const allTabs = useTabsStore((s) => s.tabs);
const activeTabIds = useTabsStore((s) => s.activeTabIds);
const focusedPaneIds = useTabsStore((s) => s.focusedPaneIds);
@@ -41,7 +61,7 @@ export function WorkspaceView() {
// Tab management shortcuts
useAppHotkey(
- "NEW_TERMINAL",
+ "NEW_GROUP",
() => {
if (activeWorkspaceId) {
addTab(activeWorkspaceId);
@@ -63,7 +83,7 @@ export function WorkspaceView() {
[focusedPaneId, removePane],
);
- // Switch between tabs (configurable shortcut)
+ // Switch between tabs (⌘+Up/Down)
useAppHotkey(
"PREV_TERMINAL",
() => {
@@ -90,7 +110,7 @@ export function WorkspaceView() {
[activeWorkspaceId, activeTabId, tabs, setActiveTab],
);
- // Switch between panes within a tab (configurable shortcut)
+ // Switch between panes within a tab (⌘+⌥+Left/Right)
useAppHotkey(
"PREV_PANE",
() => {
@@ -150,14 +170,16 @@ export function WorkspaceView() {
return (
-
-
-
+
+ {showInitView && activeWorkspaceId ? (
+
+ ) : (
+
+ )}
);
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspaceRow/WorkspaceRow.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspaceRow/WorkspaceRow.tsx
new file mode 100644
index 000000000..8f06de0e4
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspaceRow/WorkspaceRow.tsx
@@ -0,0 +1,147 @@
+import { cn } from "@superset/ui/utils";
+import { useState } from "react";
+import {
+ LuArrowRight,
+ LuGitBranch,
+ LuGitFork,
+ LuRotateCw,
+} from "react-icons/lu";
+import { trpc } from "renderer/lib/trpc";
+import type { WorkspaceItem } from "../types";
+import { getRelativeTime } from "../utils";
+
+const GITHUB_STATUS_STALE_TIME = 5 * 60 * 1000; // 5 minutes
+
+interface WorkspaceRowProps {
+ workspace: WorkspaceItem;
+ isActive: boolean;
+ onSwitch: () => void;
+ onReopen: () => void;
+ isOpening?: boolean;
+}
+
+export function WorkspaceRow({
+ workspace,
+ isActive,
+ onSwitch,
+ onReopen,
+ isOpening,
+}: WorkspaceRowProps) {
+ const isBranch = workspace.type === "branch";
+ const [hasHovered, setHasHovered] = useState(false);
+
+ // Lazy-load GitHub status on hover to avoid N+1 queries
+ const { data: githubStatus } = trpc.workspaces.getGitHubStatus.useQuery(
+ { workspaceId: workspace.workspaceId ?? "" },
+ {
+ enabled:
+ hasHovered && workspace.type === "worktree" && !!workspace.workspaceId,
+ staleTime: GITHUB_STATUS_STALE_TIME,
+ },
+ );
+
+ const pr = githubStatus?.pr;
+ const showDiffStats = pr && (pr.additions > 0 || pr.deletions > 0);
+
+ const timeText = workspace.isOpen
+ ? `Opened ${getRelativeTime(workspace.lastOpenedAt)}`
+ : `Created ${getRelativeTime(workspace.createdAt)}`;
+
+ const handleClick = () => {
+ if (workspace.isOpen) {
+ onSwitch();
+ } else {
+ onReopen();
+ }
+ };
+
+ return (
+
!hasHovered && setHasHovered(true)}
+ className={cn(
+ "flex items-center gap-3 w-full px-4 py-2 group text-left",
+ "hover:bg-background/50 transition-colors",
+ isActive && "bg-background/70",
+ isOpening && "opacity-50 cursor-wait",
+ )}
+ >
+ {/* Icon */}
+
+ {isBranch ? (
+
+ ) : (
+
+ )}
+
+
+ {/* Workspace/branch name */}
+
+ {workspace.name}
+
+
+ {/* Active indicator */}
+ {isActive && (
+
+ )}
+
+ {/* Unread indicator */}
+ {workspace.isUnread && !isActive && (
+
+
+
+
+ )}
+
+ {/* Diff stats */}
+ {showDiffStats && (
+
+ +{pr.additions}
+ -{pr.deletions}
+
+ )}
+
+ {/* Spacer */}
+
+
+ {/* Time context */}
+
+ {timeText}
+
+
+ {/* Action indicator - visible on hover */}
+
+ {isOpening ? (
+ <>
+
+ Opening...
+ >
+ ) : workspace.isOpen ? (
+ <>
+ Switch to
+
+ >
+ ) : (
+ <>
+ Reopen
+
+ >
+ )}
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspaceRow/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspaceRow/index.ts
new file mode 100644
index 000000000..0a45a4a84
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspaceRow/index.ts
@@ -0,0 +1 @@
+export * from "./WorkspaceRow";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspacesListView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspacesListView.tsx
new file mode 100644
index 000000000..8a3d50a2c
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/WorkspacesListView.tsx
@@ -0,0 +1,282 @@
+import { Button } from "@superset/ui/button";
+import { Input } from "@superset/ui/input";
+import { toast } from "@superset/ui/sonner";
+import { cn } from "@superset/ui/utils";
+import { useMemo, useState } from "react";
+import { LuSearch, LuX } from "react-icons/lu";
+import { trpc } from "renderer/lib/trpc";
+import { useSetActiveWorkspace } from "renderer/react-query/workspaces";
+import { useCloseWorkspacesList } from "renderer/stores/app-state";
+import type { FilterMode, ProjectGroup, WorkspaceItem } from "./types";
+import { WorkspaceRow } from "./WorkspaceRow";
+
+const FILTER_OPTIONS: { value: FilterMode; label: string }[] = [
+ { value: "all", label: "All" },
+ { value: "active", label: "Active" },
+ { value: "closed", label: "Closed" },
+];
+
+export function WorkspacesListView() {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [filterMode, setFilterMode] = useState
("all");
+ const utils = trpc.useUtils();
+
+ // Fetch all data
+ const { data: groups = [] } = trpc.workspaces.getAllGrouped.useQuery();
+ const { data: allProjects = [] } = trpc.projects.getRecents.useQuery();
+ const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery();
+
+ // Fetch worktrees for all projects
+ const worktreeQueries = trpc.useQueries((t) =>
+ allProjects.map((project) =>
+ t.workspaces.getWorktreesByProject({ projectId: project.id }),
+ ),
+ );
+
+ const setActiveWorkspace = useSetActiveWorkspace();
+ const closeWorkspacesList = useCloseWorkspacesList();
+
+ const openWorktree = trpc.workspaces.openWorktree.useMutation({
+ onSuccess: () => {
+ utils.workspaces.getAllGrouped.invalidate();
+ utils.workspaces.getActive.invalidate();
+ closeWorkspacesList();
+ },
+ onError: (error) => {
+ toast.error(`Failed to open workspace: ${error.message}`);
+ },
+ });
+
+ // Combine open workspaces and closed worktrees into a single list
+ const allItems = useMemo(() => {
+ const items: WorkspaceItem[] = [];
+
+ // First, add all open workspaces from groups
+ for (const group of groups) {
+ for (const ws of group.workspaces) {
+ items.push({
+ uniqueId: ws.id,
+ workspaceId: ws.id,
+ worktreeId: null,
+ projectId: ws.projectId,
+ projectName: group.project.name,
+ worktreePath: ws.worktreePath,
+ type: ws.type,
+ branch: ws.branch,
+ name: ws.name,
+ lastOpenedAt: ws.lastOpenedAt,
+ createdAt: ws.createdAt,
+ isUnread: ws.isUnread,
+ isOpen: true,
+ });
+ }
+ }
+
+ // Add closed worktrees (those without active workspaces)
+ for (let i = 0; i < allProjects.length; i++) {
+ const project = allProjects[i];
+ const worktrees = worktreeQueries[i]?.data;
+
+ if (!worktrees) continue;
+
+ for (const wt of worktrees) {
+ // Skip if this worktree has an active workspace
+ if (wt.hasActiveWorkspace) continue;
+
+ items.push({
+ uniqueId: `wt-${wt.id}`,
+ workspaceId: null,
+ worktreeId: wt.id,
+ projectId: project.id,
+ projectName: project.name,
+ worktreePath: wt.path,
+ type: "worktree",
+ branch: wt.branch,
+ name: wt.branch,
+ lastOpenedAt: wt.createdAt,
+ createdAt: wt.createdAt,
+ isUnread: false,
+ isOpen: false,
+ });
+ }
+ }
+
+ return items;
+ }, [groups, allProjects, worktreeQueries]);
+
+ // Filter by search query and filter mode
+ const filteredItems = useMemo(() => {
+ let items = allItems;
+
+ // Apply filter mode
+ if (filterMode === "active") {
+ items = items.filter((ws) => ws.isOpen);
+ } else if (filterMode === "closed") {
+ items = items.filter((ws) => !ws.isOpen);
+ }
+
+ // Apply search filter
+ if (searchQuery.trim()) {
+ const query = searchQuery.toLowerCase();
+ items = items.filter(
+ (ws) =>
+ ws.name.toLowerCase().includes(query) ||
+ ws.projectName.toLowerCase().includes(query) ||
+ ws.branch.toLowerCase().includes(query),
+ );
+ }
+
+ return items;
+ }, [allItems, searchQuery, filterMode]);
+
+ // Group by project
+ const projectGroups = useMemo(() => {
+ const groupsMap = new Map();
+
+ for (const item of filteredItems) {
+ if (!groupsMap.has(item.projectId)) {
+ groupsMap.set(item.projectId, {
+ projectId: item.projectId,
+ projectName: item.projectName,
+ workspaces: [],
+ });
+ }
+ groupsMap.get(item.projectId)?.workspaces.push(item);
+ }
+
+ // Sort workspaces within each group: active first, then by lastOpenedAt
+ for (const group of groupsMap.values()) {
+ group.workspaces.sort((a, b) => {
+ // Active workspaces first
+ if (a.isOpen !== b.isOpen) return a.isOpen ? -1 : 1;
+ // Then by most recently opened/created
+ return b.lastOpenedAt - a.lastOpenedAt;
+ });
+ }
+
+ // Sort groups by most recent activity
+ return Array.from(groupsMap.values()).sort((a, b) => {
+ const aRecent = Math.max(...a.workspaces.map((w) => w.lastOpenedAt));
+ const bRecent = Math.max(...b.workspaces.map((w) => w.lastOpenedAt));
+ return bRecent - aRecent;
+ });
+ }, [filteredItems]);
+
+ const handleSwitch = (item: WorkspaceItem) => {
+ if (item.workspaceId) {
+ setActiveWorkspace.mutate({ id: item.workspaceId });
+ closeWorkspacesList();
+ }
+ };
+
+ const handleReopen = (item: WorkspaceItem) => {
+ if (item.worktreeId) {
+ openWorktree.mutate({ worktreeId: item.worktreeId });
+ }
+ };
+
+ // Count stats for filter badges
+ const activeCount = allItems.filter((w) => w.isOpen).length;
+ const closedCount = allItems.filter((w) => !w.isOpen).length;
+
+ return (
+
+ {/* Header */}
+
+ {/* Filter toggle */}
+
+ {FILTER_OPTIONS.map((option) => {
+ const count =
+ option.value === "all"
+ ? allItems.length
+ : option.value === "active"
+ ? activeCount
+ : closedCount;
+ return (
+ setFilterMode(option.value)}
+ className={cn(
+ "px-2 py-1 text-xs rounded transition-colors",
+ filterMode === option.value
+ ? "bg-accent text-foreground"
+ : "text-foreground/60 hover:text-foreground",
+ )}
+ >
+ {option.label}
+ {count}
+
+ );
+ })}
+
+
+ {/* Search */}
+
+
+ setSearchQuery(e.target.value)}
+ className="pl-9 h-8 bg-background/50"
+ />
+
+
+ {/* Close button */}
+
+
+
+
+
+ {/* Workspaces list grouped by project */}
+
+ {projectGroups.map((group) => (
+
+ {/* Project header */}
+
+
+ {group.projectName}
+
+
+ {group.workspaces.length}
+
+
+
+ {/* Workspaces in this project */}
+ {group.workspaces.map((ws) => (
+
handleSwitch(ws)}
+ onReopen={() => handleReopen(ws)}
+ isOpening={
+ openWorktree.isPending &&
+ openWorktree.variables?.worktreeId === ws.worktreeId
+ }
+ />
+ ))}
+
+ ))}
+
+ {filteredItems.length === 0 && (
+
+ {searchQuery
+ ? "No workspaces match your search"
+ : filterMode === "active"
+ ? "No active workspaces"
+ : filterMode === "closed"
+ ? "No closed workspaces"
+ : "No workspaces yet"}
+
+ )}
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/index.ts
new file mode 100644
index 000000000..7d55b16cb
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/index.ts
@@ -0,0 +1 @@
+export { WorkspacesListView } from "./WorkspacesListView";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/types.ts b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/types.ts
new file mode 100644
index 000000000..587000def
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/types.ts
@@ -0,0 +1,26 @@
+export interface WorkspaceItem {
+ // Unique identifier - either workspace id or worktree id for closed ones
+ uniqueId: string;
+ // If open, this is the workspace id
+ workspaceId: string | null;
+ // For closed worktrees, this is the worktree id
+ worktreeId: string | null;
+ projectId: string;
+ projectName: string;
+ worktreePath: string;
+ type: "worktree" | "branch";
+ branch: string;
+ name: string;
+ lastOpenedAt: number;
+ createdAt: number;
+ isUnread: boolean;
+ isOpen: boolean;
+}
+
+export interface ProjectGroup {
+ projectId: string;
+ projectName: string;
+ workspaces: WorkspaceItem[];
+}
+
+export type FilterMode = "all" | "active" | "closed";
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/utils.ts b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/utils.ts
new file mode 100644
index 000000000..1640b1688
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspacesListView/utils.ts
@@ -0,0 +1,42 @@
+// Time unit constants (in milliseconds)
+const MS_PER_SECOND = 1000;
+const MS_PER_MINUTE = MS_PER_SECOND * 60;
+const MS_PER_HOUR = MS_PER_MINUTE * 60;
+const MS_PER_DAY = MS_PER_HOUR * 24;
+
+// Time threshold constants (in their respective units)
+const MINUTES_PER_HOUR = 60;
+const HOURS_PER_DAY = 24;
+const DAYS_PER_WEEK = 7;
+const DAYS_PER_MONTH = 30;
+const DAYS_PER_YEAR = 365;
+
+// Relative time display thresholds (in days)
+const TWO_WEEKS_DAYS = 14;
+const TWO_MONTHS_DAYS = 60;
+
+/**
+ * Returns a human-readable relative time string
+ * e.g., "2 hours ago", "yesterday", "3 days ago"
+ */
+export function getRelativeTime(timestamp: number): string {
+ const now = Date.now();
+ const diff = now - timestamp;
+
+ const minutes = Math.floor(diff / MS_PER_MINUTE);
+ const hours = Math.floor(diff / MS_PER_HOUR);
+ const days = Math.floor(diff / MS_PER_DAY);
+
+ if (minutes < 1) return "just now";
+ if (minutes < MINUTES_PER_HOUR) return `${minutes}m ago`;
+ if (hours < HOURS_PER_DAY) return `${hours}h ago`;
+ if (days === 1) return "yesterday";
+ if (days < DAYS_PER_WEEK) return `${days} days ago`;
+ if (days < TWO_WEEKS_DAYS) return "1 week ago";
+ if (days < DAYS_PER_MONTH)
+ return `${Math.floor(days / DAYS_PER_WEEK)} weeks ago`;
+ if (days < TWO_MONTHS_DAYS) return "1 month ago";
+ if (days < DAYS_PER_YEAR)
+ return `${Math.floor(days / DAYS_PER_MONTH)} months ago`;
+ return "over a year ago";
+}
diff --git a/apps/desktop/src/renderer/screens/main/hooks/index.ts b/apps/desktop/src/renderer/screens/main/hooks/index.ts
index 8337712ea..8b8a83fbc 100644
--- a/apps/desktop/src/renderer/screens/main/hooks/index.ts
+++ b/apps/desktop/src/renderer/screens/main/hooks/index.ts
@@ -1 +1,2 @@
-//
+export { usePRStatus } from "./usePRStatus";
+export { useWorkspaceRename } from "./useWorkspaceRename";
diff --git a/apps/desktop/src/renderer/screens/main/hooks/usePRStatus/index.ts b/apps/desktop/src/renderer/screens/main/hooks/usePRStatus/index.ts
new file mode 100644
index 000000000..f552343f7
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/hooks/usePRStatus/index.ts
@@ -0,0 +1 @@
+export { usePRStatus } from "./usePRStatus";
diff --git a/apps/desktop/src/renderer/screens/main/hooks/usePRStatus/usePRStatus.ts b/apps/desktop/src/renderer/screens/main/hooks/usePRStatus/usePRStatus.ts
new file mode 100644
index 000000000..713d7ff91
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/hooks/usePRStatus/usePRStatus.ts
@@ -0,0 +1,46 @@
+import type { GitHubStatus } from "@superset/local-db";
+import { trpc } from "renderer/lib/trpc";
+
+interface UsePRStatusOptions {
+ workspaceId: string | undefined;
+ enabled?: boolean;
+ refetchInterval?: number;
+}
+
+interface UsePRStatusResult {
+ pr: GitHubStatus["pr"] | null;
+ repoUrl: string | null;
+ branchExistsOnRemote: boolean;
+ isLoading: boolean;
+ refetch: () => void;
+}
+
+/**
+ * Hook to fetch and manage GitHub PR status for a workspace.
+ * Returns PR info, loading state, and refetch function.
+ */
+export function usePRStatus({
+ workspaceId,
+ enabled = true,
+ refetchInterval,
+}: UsePRStatusOptions): UsePRStatusResult {
+ const {
+ data: githubStatus,
+ isLoading,
+ refetch,
+ } = trpc.workspaces.getGitHubStatus.useQuery(
+ { workspaceId: workspaceId ?? "" },
+ {
+ enabled: enabled && !!workspaceId,
+ refetchInterval,
+ },
+ );
+
+ return {
+ pr: githubStatus?.pr ?? null,
+ repoUrl: githubStatus?.repoUrl ?? null,
+ branchExistsOnRemote: githubStatus?.branchExistsOnRemote ?? false,
+ isLoading,
+ refetch,
+ };
+}
diff --git a/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/index.ts b/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/index.ts
new file mode 100644
index 000000000..4b8035beb
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/index.ts
@@ -0,0 +1 @@
+export { useWorkspaceRename } from "./useWorkspaceRename";
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/useWorkspaceRename.ts b/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/useWorkspaceRename.ts
similarity index 100%
rename from apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/useWorkspaceRename.ts
rename to apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/useWorkspaceRename.ts
diff --git a/apps/desktop/src/renderer/screens/main/index.tsx b/apps/desktop/src/renderer/screens/main/index.tsx
index 98f2cc947..e339381a8 100644
--- a/apps/desktop/src/renderer/screens/main/index.tsx
+++ b/apps/desktop/src/renderer/screens/main/index.tsx
@@ -19,6 +19,8 @@ import { useTabsStore } from "renderer/stores/tabs/store";
import type { Tab } from "renderer/stores/tabs/types";
import { useAgentHookListener } from "renderer/stores/tabs/useAgentHookListener";
import { findPanePath, getFirstPaneId } from "renderer/stores/tabs/utils";
+import { useWorkspaceInitStore } from "renderer/stores/workspace-init";
+import { useWorkspaceSidebarStore } from "renderer/stores/workspace-sidebar-state";
import { dragDropManager } from "../../lib/dnd";
import { AppFrame } from "./components/AppFrame";
import { Background } from "./components/Background";
@@ -26,6 +28,9 @@ import { SettingsView } from "./components/SettingsView";
import { StartView } from "./components/StartView";
import { TasksView } from "./components/TasksView";
import { TopBar } from "./components/TopBar";
+import { WorkspaceInitEffects } from "./components/WorkspaceInitEffects";
+import { ResizableWorkspaceSidebar } from "./components/WorkspaceSidebar";
+import { WorkspacesListView } from "./components/WorkspacesListView";
import { WorkspaceView } from "./components/WorkspaceView";
function LoadingSpinner() {
@@ -54,12 +59,27 @@ export function MainScreen() {
onData: () => utils.auth.getState.invalidate(),
});
+ // Subscribe to workspace initialization progress
+ const updateInitProgress = useWorkspaceInitStore((s) => s.updateProgress);
+ trpc.workspaces.onInitProgress.useSubscription(undefined, {
+ onData: (progress) => {
+ updateInitProgress(progress);
+ // Invalidate workspace queries when initialization completes or fails
+ if (progress.step === "ready" || progress.step === "failed") {
+ utils.workspaces.getActive.invalidate();
+ utils.workspaces.getAllGrouped.invalidate();
+ }
+ },
+ });
+
const currentView = useCurrentView();
const openSettings = useOpenSettings();
- const { toggleSidebar } = useSidebarStore();
+ const toggleSidebar = useSidebarStore((s) => s.toggleSidebar);
+ const toggleWorkspaceSidebar = useWorkspaceSidebarStore((s) => s.toggleOpen);
const hasTasksAccess = useFeatureFlagEnabled(
FEATURE_FLAGS.ELECTRIC_TASKS_ACCESS,
);
+
const {
data: activeWorkspace,
isLoading: isWorkspaceLoading,
@@ -111,6 +131,15 @@ export function MainScreen() {
[toggleSidebar, isWorkspaceView],
);
+ useAppHotkey(
+ "TOGGLE_WORKSPACE_SIDEBAR",
+ () => {
+ toggleWorkspaceSidebar();
+ },
+ undefined,
+ [toggleWorkspaceSidebar],
+ );
+
/**
* Resolves the target pane for split operations.
* If the focused pane is desynced from layout (e.g., was removed),
@@ -267,6 +296,9 @@ export function MainScreen() {
if (currentView === "tasks" && hasTasksAccess) {
return ;
}
+ if (currentView === "workspaces-list") {
+ return ;
+ }
return ;
};
@@ -337,12 +369,16 @@ export function MainScreen() {
) : (
-
{renderContent()}
+
+
+ {renderContent()}
+
)}
+
);
}
diff --git a/apps/desktop/src/renderer/screens/sign-in/index.tsx b/apps/desktop/src/renderer/screens/sign-in/index.tsx
index 3fe2118f9..032ebb07a 100644
--- a/apps/desktop/src/renderer/screens/sign-in/index.tsx
+++ b/apps/desktop/src/renderer/screens/sign-in/index.tsx
@@ -1,10 +1,11 @@
-import { type AuthProvider, COMPANY } from "@superset/shared/constants";
+import { COMPANY } from "@superset/shared/constants";
import { Button } from "@superset/ui/button";
import { useEffect } from "react";
import { FaGithub } from "react-icons/fa";
import { FcGoogle } from "react-icons/fc";
import { posthog } from "renderer/lib/posthog";
import { trpc } from "renderer/lib/trpc";
+import type { AuthProvider } from "shared/auth";
import { SupersetLogo } from "./components/SupersetLogo";
export function SignInScreen() {
diff --git a/apps/desktop/src/renderer/stores/app-state.ts b/apps/desktop/src/renderer/stores/app-state.ts
index 296752c34..ba6ffcd1c 100644
--- a/apps/desktop/src/renderer/stores/app-state.ts
+++ b/apps/desktop/src/renderer/stores/app-state.ts
@@ -1,7 +1,7 @@
import { create } from "zustand";
import { devtools } from "zustand/middleware";
-export type AppView = "workspace" | "settings" | "tasks";
+export type AppView = "workspace" | "settings" | "tasks" | "workspaces-list";
export type SettingsSection =
| "account"
| "project"
@@ -16,6 +16,7 @@ interface AppState {
currentView: AppView;
isSettingsTabOpen: boolean;
isTasksTabOpen: boolean;
+ isWorkspacesListOpen: boolean;
settingsSection: SettingsSection;
setView: (view: AppView) => void;
openSettings: (section?: SettingsSection) => void;
@@ -24,6 +25,8 @@ interface AppState {
setSettingsSection: (section: SettingsSection) => void;
openTasks: () => void;
closeTasks: () => void;
+ openWorkspacesList: () => void;
+ closeWorkspacesList: () => void;
}
export const useAppStore = create()(
@@ -32,6 +35,7 @@ export const useAppStore = create()(
currentView: "workspace",
isSettingsTabOpen: false,
isTasksTabOpen: false,
+ isWorkspacesListOpen: false,
settingsSection: "project",
setView: (view) => {
@@ -65,6 +69,14 @@ export const useAppStore = create()(
closeTasks: () => {
set({ currentView: "workspace", isTasksTabOpen: false });
},
+
+ openWorkspacesList: () => {
+ set({ currentView: "workspaces-list", isWorkspacesListOpen: true });
+ },
+
+ closeWorkspacesList: () => {
+ set({ currentView: "workspace", isWorkspacesListOpen: false });
+ },
}),
{ name: "AppStore" },
),
@@ -87,3 +99,7 @@ export const useCloseSettingsTab = () =>
useAppStore((state) => state.closeSettingsTab);
export const useOpenTasks = () => useAppStore((state) => state.openTasks);
export const useCloseTasks = () => useAppStore((state) => state.closeTasks);
+export const useOpenWorkspacesList = () =>
+ useAppStore((state) => state.openWorkspacesList);
+export const useCloseWorkspacesList = () =>
+ useAppStore((state) => state.closeWorkspacesList);
diff --git a/apps/desktop/src/renderer/stores/index.ts b/apps/desktop/src/renderer/stores/index.ts
index b289f0bfb..824bd5107 100644
--- a/apps/desktop/src/renderer/stores/index.ts
+++ b/apps/desktop/src/renderer/stores/index.ts
@@ -6,3 +6,5 @@ export * from "./ringtone";
export * from "./sidebar-state";
export * from "./tabs";
export * from "./theme";
+export * from "./workspace-init";
+export * from "./workspace-sidebar-state";
diff --git a/apps/desktop/src/renderer/stores/new-workspace-modal.ts b/apps/desktop/src/renderer/stores/new-workspace-modal.ts
index 0890c7797..38b18916b 100644
--- a/apps/desktop/src/renderer/stores/new-workspace-modal.ts
+++ b/apps/desktop/src/renderer/stores/new-workspace-modal.ts
@@ -3,7 +3,8 @@ import { devtools } from "zustand/middleware";
interface NewWorkspaceModalState {
isOpen: boolean;
- openModal: () => void;
+ preSelectedProjectId: string | null;
+ openModal: (projectId?: string) => void;
closeModal: () => void;
}
@@ -11,13 +12,14 @@ export const useNewWorkspaceModalStore = create()(
devtools(
(set) => ({
isOpen: false,
+ preSelectedProjectId: null,
- openModal: () => {
- set({ isOpen: true });
+ openModal: (projectId?: string) => {
+ set({ isOpen: true, preSelectedProjectId: projectId ?? null });
},
closeModal: () => {
- set({ isOpen: false });
+ set({ isOpen: false, preSelectedProjectId: null });
},
}),
{ name: "NewWorkspaceModalStore" },
@@ -31,3 +33,5 @@ export const useOpenNewWorkspaceModal = () =>
useNewWorkspaceModalStore((state) => state.openModal);
export const useCloseNewWorkspaceModal = () =>
useNewWorkspaceModalStore((state) => state.closeModal);
+export const usePreSelectedProjectId = () =>
+ useNewWorkspaceModalStore((state) => state.preSelectedProjectId);
diff --git a/apps/desktop/src/renderer/stores/tabs/store.ts b/apps/desktop/src/renderer/stores/tabs/store.ts
index d28d8316a..828d0679d 100644
--- a/apps/desktop/src/renderer/stores/tabs/store.ts
+++ b/apps/desktop/src/renderer/stores/tabs/store.ts
@@ -4,9 +4,10 @@ import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { trpcTabsStorage } from "../../lib/trpc-storage";
import { movePaneToNewTab, movePaneToTab } from "./actions/move-pane";
-import type { TabsState, TabsStore } from "./types";
+import type { AddFileViewerPaneOptions, TabsState, TabsStore } from "./types";
import {
type CreatePaneOptions,
+ createFileViewerPane,
createPane,
createTabWithPane,
extractPaneIdsFromLayout,
@@ -125,7 +126,11 @@ export const useTabsStore = create()(
const paneIds = getPaneIdsForTab(state.panes, tabId);
for (const paneId of paneIds) {
- killTerminalForPane(paneId);
+ // Only kill terminal sessions for terminal panes (avoids unnecessary IPC for file-viewers)
+ const pane = state.panes[paneId];
+ if (pane?.type === "terminal") {
+ killTerminalForPane(paneId);
+ }
}
const newPanes = { ...state.panes };
@@ -285,7 +290,10 @@ export const useTabsStore = create()(
const newPanes = { ...state.panes };
for (const paneId of removedPaneIds) {
- killTerminalForPane(paneId);
+ // P2: Only kill terminal for actual terminal panes (avoid unnecessary IPC)
+ if (state.panes[paneId]?.type === "terminal") {
+ killTerminalForPane(paneId);
+ }
delete newPanes[paneId];
}
@@ -340,6 +348,112 @@ export const useTabsStore = create()(
return newPane.id;
},
+ addFileViewerPane: (
+ workspaceId: string,
+ options: AddFileViewerPaneOptions,
+ ) => {
+ const state = get();
+ const activeTabId = state.activeTabIds[workspaceId];
+ const activeTab = state.tabs.find((t) => t.id === activeTabId);
+
+ // If no active tab, create a new one (this shouldn't normally happen)
+ if (!activeTab) {
+ const { tabId, paneId } = get().addTab(workspaceId);
+ // Update the pane to be a file-viewer (must use set() to get fresh state after addTab)
+ const fileViewerPane = createFileViewerPane(tabId, options);
+ set((s) => ({
+ panes: {
+ ...s.panes,
+ [paneId]: {
+ ...fileViewerPane,
+ id: paneId, // Keep the original ID
+ },
+ },
+ }));
+ return paneId;
+ }
+
+ // Look for an existing unlocked file-viewer pane in the active tab
+ const tabPaneIds = extractPaneIdsFromLayout(activeTab.layout);
+ const fileViewerPanes = tabPaneIds
+ .map((id) => state.panes[id])
+ .filter(
+ (p) =>
+ p?.type === "file-viewer" &&
+ p.fileViewer &&
+ !p.fileViewer.isLocked,
+ );
+
+ // If we found an unlocked file-viewer pane, reuse it
+ if (fileViewerPanes.length > 0) {
+ const paneToReuse = fileViewerPanes[0];
+ const fileName =
+ options.filePath.split("/").pop() || options.filePath;
+
+ // Determine default view mode
+ let viewMode: "raw" | "rendered" | "diff" = "raw";
+ if (options.diffCategory) {
+ viewMode = "diff";
+ } else if (
+ options.filePath.endsWith(".md") ||
+ options.filePath.endsWith(".markdown") ||
+ options.filePath.endsWith(".mdx")
+ ) {
+ viewMode = "rendered";
+ }
+
+ set({
+ panes: {
+ ...state.panes,
+ [paneToReuse.id]: {
+ ...paneToReuse,
+ name: fileName,
+ fileViewer: {
+ filePath: options.filePath,
+ viewMode,
+ isLocked: false,
+ diffLayout: "inline",
+ diffCategory: options.diffCategory,
+ commitHash: options.commitHash,
+ oldPath: options.oldPath,
+ initialLine: options.line,
+ initialColumn: options.column,
+ },
+ },
+ },
+ focusedPaneIds: {
+ ...state.focusedPaneIds,
+ [activeTab.id]: paneToReuse.id,
+ },
+ });
+
+ return paneToReuse.id;
+ }
+
+ // No reusable pane found, create a new one
+ const newPane = createFileViewerPane(activeTab.id, options);
+
+ const newLayout: MosaicNode = {
+ direction: "row",
+ first: activeTab.layout,
+ second: newPane.id,
+ splitPercentage: 50,
+ };
+
+ set({
+ tabs: state.tabs.map((t) =>
+ t.id === activeTab.id ? { ...t, layout: newLayout } : t,
+ ),
+ panes: { ...state.panes, [newPane.id]: newPane },
+ focusedPaneIds: {
+ ...state.focusedPaneIds,
+ [activeTab.id]: newPane.id,
+ },
+ });
+
+ return newPane.id;
+ },
+
removePane: (paneId) => {
const state = get();
const pane = state.panes[paneId];
@@ -354,7 +468,10 @@ export const useTabsStore = create()(
return;
}
- killTerminalForPane(paneId);
+ // Only kill terminal sessions for terminal panes (avoids unnecessary IPC for file-viewers)
+ if (pane.type === "terminal") {
+ killTerminalForPane(paneId);
+ }
const newLayout = removePaneFromLayout(tab.layout, paneId);
if (!newLayout) {
@@ -428,6 +545,33 @@ export const useTabsStore = create()(
}));
},
+ clearWorkspaceAttention: (workspaceId) => {
+ const state = get();
+ const workspaceTabs = state.tabs.filter(
+ (t) => t.workspaceId === workspaceId,
+ );
+ const workspacePaneIds = workspaceTabs.flatMap((t) =>
+ extractPaneIdsFromLayout(t.layout),
+ );
+
+ if (workspacePaneIds.length === 0) {
+ return;
+ }
+
+ const newPanes = { ...state.panes };
+ let hasChanges = false;
+ for (const paneId of workspacePaneIds) {
+ if (newPanes[paneId]?.needsAttention) {
+ newPanes[paneId] = { ...newPanes[paneId], needsAttention: false };
+ hasChanges = true;
+ }
+ }
+
+ if (hasChanges) {
+ set({ panes: newPanes });
+ }
+ },
+
updatePaneCwd: (paneId, cwd, confirmed) => {
set((state) => ({
panes: {
@@ -463,7 +607,19 @@ export const useTabsStore = create()(
const sourcePane = state.panes[sourcePaneId];
if (!sourcePane || sourcePane.tabId !== tabId) return;
- const newPane = createPane(tabId);
+ // Clone file-viewer panes instead of creating a terminal
+ const newPane =
+ sourcePane.type === "file-viewer" && sourcePane.fileViewer
+ ? createFileViewerPane(tabId, {
+ filePath: sourcePane.fileViewer.filePath,
+ viewMode: sourcePane.fileViewer.viewMode,
+ isLocked: true, // Lock the cloned pane
+ diffLayout: sourcePane.fileViewer.diffLayout,
+ diffCategory: sourcePane.fileViewer.diffCategory,
+ commitHash: sourcePane.fileViewer.commitHash,
+ oldPath: sourcePane.fileViewer.oldPath,
+ })
+ : createPane(tabId);
let newLayout: MosaicNode;
if (path && path.length > 0) {
@@ -511,7 +667,19 @@ export const useTabsStore = create()(
const sourcePane = state.panes[sourcePaneId];
if (!sourcePane || sourcePane.tabId !== tabId) return;
- const newPane = createPane(tabId);
+ // Clone file-viewer panes instead of creating a terminal
+ const newPane =
+ sourcePane.type === "file-viewer" && sourcePane.fileViewer
+ ? createFileViewerPane(tabId, {
+ filePath: sourcePane.fileViewer.filePath,
+ viewMode: sourcePane.fileViewer.viewMode,
+ isLocked: true, // Lock the cloned pane
+ diffLayout: sourcePane.fileViewer.diffLayout,
+ diffCategory: sourcePane.fileViewer.diffCategory,
+ commitHash: sourcePane.fileViewer.commitHash,
+ oldPath: sourcePane.fileViewer.oldPath,
+ })
+ : createPane(tabId);
let newLayout: MosaicNode;
if (path && path.length > 0) {
diff --git a/apps/desktop/src/renderer/stores/tabs/types.ts b/apps/desktop/src/renderer/stores/tabs/types.ts
index bcb0f70af..9638df6e0 100644
--- a/apps/desktop/src/renderer/stores/tabs/types.ts
+++ b/apps/desktop/src/renderer/stores/tabs/types.ts
@@ -1,4 +1,5 @@
import type { MosaicBranch, MosaicNode } from "react-mosaic-component";
+import type { ChangeCategory } from "shared/changes-types";
import type { BaseTab, BaseTabsState, Pane, PaneType } from "shared/tabs-types";
// Re-export shared types
@@ -28,6 +29,20 @@ export interface AddTabOptions {
initialCwd?: string;
}
+/**
+ * Options for opening a file in a file-viewer pane
+ */
+export interface AddFileViewerPaneOptions {
+ filePath: string;
+ diffCategory?: ChangeCategory;
+ commitHash?: string;
+ oldPath?: string;
+ /** Line to scroll to (raw mode only) */
+ line?: number;
+ /** Column to scroll to (raw mode only) */
+ column?: number;
+}
+
/**
* Actions available on the tabs store
*/
@@ -51,10 +66,15 @@ export interface TabsStore extends TabsState {
// Pane operations
addPane: (tabId: string, options?: AddTabOptions) => string;
+ addFileViewerPane: (
+ workspaceId: string,
+ options: AddFileViewerPaneOptions,
+ ) => string;
removePane: (paneId: string) => void;
setFocusedPane: (tabId: string, paneId: string) => void;
markPaneAsUsed: (paneId: string) => void;
setNeedsAttention: (paneId: string, needsAttention: boolean) => void;
+ clearWorkspaceAttention: (workspaceId: string) => void;
updatePaneCwd: (
paneId: string,
cwd: string | null,
diff --git a/apps/desktop/src/renderer/stores/tabs/utils.ts b/apps/desktop/src/renderer/stores/tabs/utils.ts
index a1e7bef16..62ee90aad 100644
--- a/apps/desktop/src/renderer/stores/tabs/utils.ts
+++ b/apps/desktop/src/renderer/stores/tabs/utils.ts
@@ -1,6 +1,14 @@
import type { MosaicBranch, MosaicNode } from "react-mosaic-component";
+import type { ChangeCategory } from "shared/changes-types";
+import type {
+ DiffLayout,
+ FileViewerMode,
+ FileViewerState,
+} from "shared/tabs-types";
import type { Pane, PaneType, Tab } from "./types";
+const MARKDOWN_EXTENSIONS = [".md", ".markdown", ".mdx"] as const;
+
/**
* Generates a unique ID with the given prefix
*/
@@ -82,6 +90,66 @@ export const createPane = (
};
};
+/**
+ * Options for creating a file-viewer pane
+ */
+export interface CreateFileViewerPaneOptions {
+ filePath: string;
+ viewMode?: FileViewerMode;
+ isLocked?: boolean;
+ diffLayout?: DiffLayout;
+ diffCategory?: ChangeCategory;
+ commitHash?: string;
+ oldPath?: string;
+ /** Line to scroll to (raw mode only) */
+ line?: number;
+ /** Column to scroll to (raw mode only) */
+ column?: number;
+}
+
+/**
+ * Creates a new file-viewer pane with the given properties
+ */
+export const createFileViewerPane = (
+ tabId: string,
+ options: CreateFileViewerPaneOptions,
+): Pane => {
+ const id = generateId("pane");
+
+ // Determine default view mode based on file and category
+ let defaultViewMode: FileViewerMode = "raw";
+ if (options.diffCategory) {
+ defaultViewMode = "diff";
+ } else if (
+ MARKDOWN_EXTENSIONS.some((ext) => options.filePath.endsWith(ext))
+ ) {
+ defaultViewMode = "rendered";
+ }
+
+ const fileViewer: FileViewerState = {
+ filePath: options.filePath,
+ viewMode: options.viewMode ?? defaultViewMode,
+ isLocked: options.isLocked ?? false,
+ diffLayout: options.diffLayout ?? "inline",
+ diffCategory: options.diffCategory,
+ commitHash: options.commitHash,
+ oldPath: options.oldPath,
+ initialLine: options.line,
+ initialColumn: options.column,
+ };
+
+ // Use filename for display name
+ const fileName = options.filePath.split("/").pop() || options.filePath;
+
+ return {
+ id,
+ tabId,
+ type: "file-viewer",
+ name: fileName,
+ fileViewer,
+ };
+};
+
/**
* Generates a static tab name based on existing tabs
* (e.g., "Terminal 1", "Terminal 2", finding the next available number)
diff --git a/apps/desktop/src/renderer/stores/workspace-init.ts b/apps/desktop/src/renderer/stores/workspace-init.ts
new file mode 100644
index 000000000..b3f4d30f5
--- /dev/null
+++ b/apps/desktop/src/renderer/stores/workspace-init.ts
@@ -0,0 +1,102 @@
+import type { WorkspaceInitProgress } from "shared/types/workspace-init";
+import { create } from "zustand";
+import { devtools } from "zustand/middleware";
+
+/**
+ * Data needed to create a terminal when workspace becomes ready.
+ * Stored globally so it survives dialog/hook unmounts.
+ */
+export interface PendingTerminalSetup {
+ workspaceId: string;
+ projectId: string;
+ initialCommands: string[] | null;
+}
+
+interface WorkspaceInitState {
+ // Map of workspaceId -> progress
+ initProgress: Record;
+
+ // Map of workspaceId -> pending terminal setup (survives dialog unmount)
+ pendingTerminalSetups: Record;
+
+ // Actions
+ updateProgress: (progress: WorkspaceInitProgress) => void;
+ clearProgress: (workspaceId: string) => void;
+ addPendingTerminalSetup: (setup: PendingTerminalSetup) => void;
+ removePendingTerminalSetup: (workspaceId: string) => void;
+}
+
+export const useWorkspaceInitStore = create()(
+ devtools(
+ (set, get) => ({
+ initProgress: {},
+ pendingTerminalSetups: {},
+
+ updateProgress: (progress) => {
+ set((state) => ({
+ initProgress: {
+ ...state.initProgress,
+ [progress.workspaceId]: progress,
+ },
+ }));
+
+ // For memory hygiene, clear "ready" progress after 5 minutes
+ // (long enough that WorkspaceInitEffects will have processed it)
+ if (progress.step === "ready") {
+ setTimeout(
+ () => {
+ const current = get().initProgress[progress.workspaceId];
+ if (current?.step === "ready") {
+ get().clearProgress(progress.workspaceId);
+ }
+ },
+ 5 * 60 * 1000,
+ ); // 5 minutes
+ }
+ },
+
+ clearProgress: (workspaceId) => {
+ set((state) => {
+ const { [workspaceId]: _, ...rest } = state.initProgress;
+ return { initProgress: rest };
+ });
+ },
+
+ addPendingTerminalSetup: (setup) => {
+ set((state) => ({
+ pendingTerminalSetups: {
+ ...state.pendingTerminalSetups,
+ [setup.workspaceId]: setup,
+ },
+ }));
+ },
+
+ removePendingTerminalSetup: (workspaceId) => {
+ set((state) => {
+ const { [workspaceId]: _, ...rest } = state.pendingTerminalSetups;
+ return { pendingTerminalSetups: rest };
+ });
+ },
+ }),
+ { name: "WorkspaceInitStore" },
+ ),
+);
+
+export const useWorkspaceInitProgress = (workspaceId: string) =>
+ useWorkspaceInitStore((state) => state.initProgress[workspaceId]);
+
+export const useIsWorkspaceInitializing = (workspaceId: string) =>
+ useWorkspaceInitStore((state) => {
+ const progress = state.initProgress[workspaceId];
+ return (
+ progress !== undefined &&
+ progress.step !== "ready" &&
+ progress.step !== "failed"
+ );
+ });
+
+export const useHasWorkspaceFailed = (workspaceId: string) =>
+ useWorkspaceInitStore((state) => {
+ const progress = state.initProgress[workspaceId];
+ return progress?.step === "failed";
+ });
diff --git a/apps/desktop/src/renderer/stores/workspace-sidebar-state.ts b/apps/desktop/src/renderer/stores/workspace-sidebar-state.ts
new file mode 100644
index 000000000..adb12801e
--- /dev/null
+++ b/apps/desktop/src/renderer/stores/workspace-sidebar-state.ts
@@ -0,0 +1,105 @@
+import { create } from "zustand";
+import { devtools, persist } from "zustand/middleware";
+
+const DEFAULT_WORKSPACE_SIDEBAR_WIDTH = 280;
+export const MIN_WORKSPACE_SIDEBAR_WIDTH = 220;
+export const MAX_WORKSPACE_SIDEBAR_WIDTH = 400;
+
+interface WorkspaceSidebarState {
+ isOpen: boolean;
+ width: number;
+ lastOpenWidth: number;
+ // Use string[] instead of Set for JSON serialization with Zustand persist
+ collapsedProjectIds: string[];
+ isResizing: boolean;
+
+ toggleOpen: () => void;
+ setOpen: (open: boolean) => void;
+ setWidth: (width: number) => void;
+ setIsResizing: (isResizing: boolean) => void;
+ toggleProjectCollapsed: (projectId: string) => void;
+ isProjectCollapsed: (projectId: string) => boolean;
+}
+
+export const useWorkspaceSidebarStore = create()(
+ devtools(
+ persist(
+ (set, get) => ({
+ isOpen: true,
+ width: DEFAULT_WORKSPACE_SIDEBAR_WIDTH,
+ lastOpenWidth: DEFAULT_WORKSPACE_SIDEBAR_WIDTH,
+ collapsedProjectIds: [],
+ isResizing: false,
+
+ toggleOpen: () => {
+ const { isOpen, lastOpenWidth } = get();
+ if (isOpen) {
+ set({ isOpen: false, width: 0 });
+ } else {
+ set({
+ isOpen: true,
+ width: lastOpenWidth,
+ });
+ }
+ },
+
+ setOpen: (open) => {
+ const { lastOpenWidth } = get();
+ set({
+ isOpen: open,
+ width: open ? lastOpenWidth : 0,
+ });
+ },
+
+ setWidth: (width) => {
+ const clampedWidth = Math.max(
+ MIN_WORKSPACE_SIDEBAR_WIDTH,
+ Math.min(MAX_WORKSPACE_SIDEBAR_WIDTH, width),
+ );
+
+ if (width > 0) {
+ set({
+ width: clampedWidth,
+ lastOpenWidth: clampedWidth,
+ isOpen: true,
+ });
+ } else {
+ set({
+ width: 0,
+ isOpen: false,
+ });
+ }
+ },
+
+ setIsResizing: (isResizing) => {
+ set({ isResizing });
+ },
+
+ toggleProjectCollapsed: (projectId) => {
+ set((state) => ({
+ collapsedProjectIds: state.collapsedProjectIds.includes(projectId)
+ ? state.collapsedProjectIds.filter((id) => id !== projectId)
+ : [...state.collapsedProjectIds, projectId],
+ }));
+ },
+
+ isProjectCollapsed: (projectId) => {
+ return get().collapsedProjectIds.includes(projectId);
+ },
+ }),
+ {
+ name: "workspace-sidebar-store",
+ version: 1,
+ // Exclude ephemeral state from persistence
+ partialize: (state) => ({
+ isOpen: state.isOpen,
+ width: state.width,
+ lastOpenWidth: state.lastOpenWidth,
+ collapsedProjectIds: state.collapsedProjectIds,
+ // isResizing intentionally excluded - ephemeral UI state
+ }),
+ },
+ ),
+ { name: "WorkspaceSidebarStore" },
+ ),
+);
diff --git a/apps/desktop/src/shared/auth.ts b/apps/desktop/src/shared/auth.ts
new file mode 100644
index 000000000..2f3348da3
--- /dev/null
+++ b/apps/desktop/src/shared/auth.ts
@@ -0,0 +1,16 @@
+export { AUTH_PROVIDERS, type AuthProvider } from "@superset/shared/constants";
+
+/**
+ * Auth session - just tokens, user data fetched separately via tRPC
+ */
+export interface AuthSession {
+ accessToken: string;
+ accessTokenExpiresAt: number;
+ refreshToken: string;
+ refreshTokenExpiresAt: number;
+}
+
+export interface SignInResult {
+ success: boolean;
+ error?: string;
+}
diff --git a/apps/desktop/src/shared/constants.ts b/apps/desktop/src/shared/constants.ts
index 6bc788cea..1ec904ae1 100644
--- a/apps/desktop/src/shared/constants.ts
+++ b/apps/desktop/src/shared/constants.ts
@@ -46,3 +46,4 @@ export const NOTIFICATION_EVENTS = {
// Default user preference values
export const DEFAULT_CONFIRM_ON_QUIT = true;
+export const DEFAULT_TERMINAL_LINK_BEHAVIOR = "external-editor" as const;
diff --git a/apps/desktop/src/shared/detect-language.ts b/apps/desktop/src/shared/detect-language.ts
new file mode 100644
index 000000000..7f305cd2b
--- /dev/null
+++ b/apps/desktop/src/shared/detect-language.ts
@@ -0,0 +1,61 @@
+export function detectLanguage(filePath: string): string {
+ const ext = filePath.split(".").pop()?.toLowerCase();
+
+ const languageMap: Record = {
+ // JavaScript/TypeScript
+ ts: "typescript",
+ tsx: "typescript",
+ js: "javascript",
+ jsx: "javascript",
+ mjs: "javascript",
+ cjs: "javascript",
+
+ // Web
+ html: "html",
+ htm: "html",
+ css: "css",
+ scss: "scss",
+ less: "less",
+
+ // Data formats
+ json: "json",
+ yaml: "yaml",
+ yml: "yaml",
+ xml: "xml",
+ toml: "toml",
+
+ // Markdown/Documentation
+ md: "markdown",
+ mdx: "markdown",
+
+ // Shell
+ sh: "shell",
+ bash: "shell",
+ zsh: "shell",
+ fish: "shell",
+
+ // Config
+ dockerfile: "dockerfile",
+ makefile: "makefile",
+
+ // Other languages
+ py: "python",
+ rb: "ruby",
+ go: "go",
+ rs: "rust",
+ java: "java",
+ kt: "kotlin",
+ swift: "swift",
+ c: "c",
+ cpp: "cpp",
+ h: "c",
+ hpp: "cpp",
+ cs: "csharp",
+ php: "php",
+ sql: "sql",
+ graphql: "graphql",
+ gql: "graphql",
+ };
+
+ return languageMap[ext || ""] || "plaintext";
+}
diff --git a/apps/desktop/src/shared/hotkeys.ts b/apps/desktop/src/shared/hotkeys.ts
index c4e38007b..c889f1127 100644
--- a/apps/desktop/src/shared/hotkeys.ts
+++ b/apps/desktop/src/shared/hotkeys.ts
@@ -410,20 +410,25 @@ export const HOTKEYS = {
category: "Workspace",
}),
PREV_WORKSPACE: defineHotkey({
- keys: "meta+left",
+ keys: "meta+up",
label: "Previous Workspace",
category: "Workspace",
}),
NEXT_WORKSPACE: defineHotkey({
- keys: "meta+right",
+ keys: "meta+down",
label: "Next Workspace",
category: "Workspace",
}),
// Layout
TOGGLE_SIDEBAR: defineHotkey({
+ keys: "meta+l",
+ label: "Toggle Changes Tab",
+ category: "Layout",
+ }),
+ TOGGLE_WORKSPACE_SIDEBAR: defineHotkey({
keys: "meta+b",
- label: "Toggle Sidebar",
+ label: "Toggle Workspaces Sidebar",
category: "Layout",
}),
SPLIT_RIGHT: defineHotkey({
@@ -444,6 +449,12 @@ export const HOTKEYS = {
category: "Layout",
description: "Split the current pane along its longer side",
}),
+ CLOSE_PANE: defineHotkey({
+ keys: "meta+w",
+ label: "Close Pane",
+ category: "Layout",
+ description: "Close the current pane",
+ }),
// Terminal
FIND_IN_TERMINAL: defineHotkey({
@@ -452,9 +463,9 @@ export const HOTKEYS = {
category: "Terminal",
description: "Search text in the active terminal",
}),
- NEW_TERMINAL: defineHotkey({
+ NEW_GROUP: defineHotkey({
keys: "meta+t",
- label: "New Terminal",
+ label: "New Tab",
category: "Terminal",
}),
CLOSE_TERMINAL: defineHotkey({
@@ -468,12 +479,12 @@ export const HOTKEYS = {
category: "Terminal",
}),
PREV_TERMINAL: defineHotkey({
- keys: "meta+up",
+ keys: "meta+left",
label: "Previous Terminal",
category: "Terminal",
}),
NEXT_TERMINAL: defineHotkey({
- keys: "meta+down",
+ keys: "meta+right",
label: "Next Terminal",
category: "Terminal",
}),
@@ -575,6 +586,15 @@ export function getDefaultHotkey(
return HOTKEYS[id].defaults[platform];
}
+/**
+ * Get the hotkey binding for the current platform.
+ * Convenience wrapper around getDefaultHotkey.
+ * Returns empty string if no hotkey is defined (safe for useHotkeys).
+ */
+export function getHotkey(id: HotkeyId): string {
+ return getDefaultHotkey(id, getCurrentPlatform()) ?? "";
+}
+
export function getEffectiveHotkey(
id: HotkeyId,
overrides: Partial>,
diff --git a/apps/desktop/src/shared/tabs-types.ts b/apps/desktop/src/shared/tabs-types.ts
index 8ae323601..d8c921186 100644
--- a/apps/desktop/src/shared/tabs-types.ts
+++ b/apps/desktop/src/shared/tabs-types.ts
@@ -3,10 +3,46 @@
* Renderer extends these with MosaicNode layout specifics.
*/
+import type { ChangeCategory } from "./changes-types";
+
/**
* Pane types that can be displayed within a tab
*/
-export type PaneType = "terminal" | "webview";
+export type PaneType = "terminal" | "webview" | "file-viewer";
+
+/**
+ * File viewer display modes
+ */
+export type FileViewerMode = "rendered" | "raw" | "diff";
+
+/**
+ * Diff layout options for file viewer
+ */
+export type DiffLayout = "inline" | "side-by-side";
+
+/**
+ * File viewer pane-specific properties
+ */
+export interface FileViewerState {
+ /** Worktree-relative file path */
+ filePath: string;
+ /** Display mode: rendered (markdown), raw (source), or diff */
+ viewMode: FileViewerMode;
+ /** If true, this pane won't be reused for new file clicks */
+ isLocked: boolean;
+ /** Diff display layout */
+ diffLayout: DiffLayout;
+ /** Category for diff source (against-main, committed, staged, unstaged) */
+ diffCategory?: ChangeCategory;
+ /** Commit hash for committed category diffs */
+ commitHash?: string;
+ /** Original path for renamed files */
+ oldPath?: string;
+ /** Initial line to scroll to (raw mode only, transient - applied once) */
+ initialLine?: number;
+ /** Initial column to scroll to (raw mode only, transient - applied once) */
+ initialColumn?: number;
+}
/**
* Base Pane interface - shared between main and renderer
@@ -23,6 +59,7 @@ export interface Pane {
url?: string; // For webview panes
cwd?: string | null; // Current working directory
cwdConfirmed?: boolean; // True if cwd confirmed via OSC-7, false if seeded
+ fileViewer?: FileViewerState; // For file-viewer panes
}
/**
diff --git a/apps/desktop/src/shared/types/index.ts b/apps/desktop/src/shared/types/index.ts
index a47714c62..72711f77a 100644
--- a/apps/desktop/src/shared/types/index.ts
+++ b/apps/desktop/src/shared/types/index.ts
@@ -5,4 +5,5 @@ export * from "./mosaic";
export * from "./ports";
export * from "./tab";
export * from "./workspace";
+export * from "./workspace-init";
export * from "./worktree";
diff --git a/apps/desktop/src/shared/types/workspace-init.ts b/apps/desktop/src/shared/types/workspace-init.ts
new file mode 100644
index 000000000..b19d22a42
--- /dev/null
+++ b/apps/desktop/src/shared/types/workspace-init.ts
@@ -0,0 +1,72 @@
+/**
+ * Workspace initialization progress types.
+ * Used for streaming progress updates during workspace creation.
+ */
+
+export type WorkspaceInitStep =
+ | "pending"
+ | "syncing" // Syncing with remote
+ | "verifying" // Verifying base branch exists
+ | "fetching" // Fetching latest changes
+ | "creating_worktree" // Creating git worktree
+ | "copying_config" // Copying .superset configuration
+ | "finalizing" // Final DB operations
+ | "ready"
+ | "failed";
+
+export interface WorkspaceInitProgress {
+ workspaceId: string;
+ projectId: string;
+ step: WorkspaceInitStep;
+ message: string;
+ error?: string;
+}
+
+export const INIT_STEP_MESSAGES: Record = {
+ pending: "Preparing...",
+ syncing: "Syncing with remote...",
+ verifying: "Verifying base branch...",
+ fetching: "Fetching latest changes...",
+ creating_worktree: "Creating git worktree...",
+ copying_config: "Copying configuration...",
+ finalizing: "Finalizing setup...",
+ ready: "Ready",
+ failed: "Failed",
+};
+
+/**
+ * Order of steps for UI progress display.
+ * Used to show completed/current/pending steps in the progress view.
+ */
+export const INIT_STEP_ORDER: WorkspaceInitStep[] = [
+ "pending",
+ "syncing",
+ "verifying",
+ "fetching",
+ "creating_worktree",
+ "copying_config",
+ "finalizing",
+ "ready",
+];
+
+/**
+ * Get the index of a step in the progress order.
+ * Returns -1 for "failed" since it's not part of the normal flow.
+ */
+export function getStepIndex(step: WorkspaceInitStep): number {
+ if (step === "failed") return -1;
+ return INIT_STEP_ORDER.indexOf(step);
+}
+
+/**
+ * Check if a step is complete based on the current step.
+ */
+export function isStepComplete(
+ step: WorkspaceInitStep,
+ currentStep: WorkspaceInitStep,
+): boolean {
+ if (currentStep === "failed") return false;
+ const stepIndex = getStepIndex(step);
+ const currentIndex = getStepIndex(currentStep);
+ return stepIndex < currentIndex;
+}
diff --git a/apps/desktop/test-setup.ts b/apps/desktop/test-setup.ts
index b14e1c4af..2bf5535b0 100644
--- a/apps/desktop/test-setup.ts
+++ b/apps/desktop/test-setup.ts
@@ -118,7 +118,14 @@ mock.module("electron", () => ({
screen: {
getPrimaryDisplay: mock(() => ({
workAreaSize: { width: 1920, height: 1080 },
+ bounds: { x: 0, y: 0, width: 1920, height: 1080 },
})),
+ getAllDisplays: mock(() => [
+ {
+ bounds: { x: 0, y: 0, width: 1920, height: 1080 },
+ workAreaSize: { width: 1920, height: 1080 },
+ },
+ ]),
},
Notification: mock(() => ({
show: mock(),
diff --git a/apps/marketing/package.json b/apps/marketing/package.json
index af17cd237..52fab968e 100644
--- a/apps/marketing/package.json
+++ b/apps/marketing/package.json
@@ -11,10 +11,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@clerk/nextjs": "^6.36.2",
"@react-three/drei": "^10.7.6",
"@react-three/fiber": "^9.4.0",
"@sentry/nextjs": "^10.32.1",
- "@superset/auth": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/ui": "workspace:*",
"@t3-oss/env-nextjs": "^0.13.8",
diff --git a/apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx b/apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
index 2c0c76855..53f41fa7d 100644
--- a/apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
+++ b/apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
@@ -1,14 +1,13 @@
-import { auth } from "@superset/auth";
+import { auth } from "@clerk/nextjs/server";
import { DOWNLOAD_URL_MAC_ARM64 } from "@superset/shared/constants";
import { Download } from "lucide-react";
-import { headers } from "next/headers";
import { env } from "@/env";
export async function CTAButtons() {
- const session = await auth.api.getSession({ headers: await headers() });
+ const { userId } = await auth();
- if (session) {
+ if (userId) {
return (
<>
) {
return (
-
-
-
-
-
-
- } />
- {children}
-
-
-
-
-
+
+
+
+
+
+
+
+ } />
+ {children}
+
+
+
+
+
+
);
}
diff --git a/apps/marketing/src/env.ts b/apps/marketing/src/env.ts
index f1312f39e..a8fc0d027 100644
--- a/apps/marketing/src/env.ts
+++ b/apps/marketing/src/env.ts
@@ -10,11 +10,14 @@ export const env = createEnv({
.default("development"),
},
server: {
+ CLERK_SECRET_KEY: z.string(),
SENTRY_AUTH_TOKEN: z.string().optional(),
},
client: {
NEXT_PUBLIC_API_URL: z.string().url(),
NEXT_PUBLIC_WEB_URL: z.string().url(),
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string(),
+ NEXT_PUBLIC_COOKIE_DOMAIN: z.string(),
NEXT_PUBLIC_POSTHOG_KEY: z.string(),
NEXT_PUBLIC_POSTHOG_HOST: z.string().url(),
NEXT_PUBLIC_SENTRY_DSN_MARKETING: z.string().optional(),
@@ -26,6 +29,9 @@ export const env = createEnv({
NODE_ENV: process.env.NODE_ENV,
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
NEXT_PUBLIC_WEB_URL: process.env.NEXT_PUBLIC_WEB_URL,
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:
+ process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
+ NEXT_PUBLIC_COOKIE_DOMAIN: process.env.NEXT_PUBLIC_COOKIE_DOMAIN,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
NEXT_PUBLIC_SENTRY_DSN_MARKETING:
diff --git a/apps/marketing/src/proxy.ts b/apps/marketing/src/proxy.ts
new file mode 100644
index 000000000..29446eb2b
--- /dev/null
+++ b/apps/marketing/src/proxy.ts
@@ -0,0 +1,10 @@
+import { clerkMiddleware } from "@clerk/nextjs/server";
+
+export default clerkMiddleware();
+
+export const config = {
+ matcher: [
+ "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
+ "/(api|trpc)(.*)",
+ ],
+};
diff --git a/apps/web/package.json b/apps/web/package.json
index b95e05f91..ee74c1742 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -11,8 +11,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@clerk/nextjs": "^6.36.2",
"@sentry/nextjs": "^10.32.1",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/trpc": "workspace:*",
@@ -24,7 +24,6 @@
"@trpc/server": "^11.7.1",
"@trpc/tanstack-react-query": "^11.7.1",
"@uiw/react-md-editor": "^4.0.11",
- "better-auth": "^1.4.9",
"framer-motion": "^12.23.26",
"geist": "^1.5.1",
"import-in-the-middle": "2.0.1",
diff --git a/apps/web/src/app/(auth)/layout.tsx b/apps/web/src/app/(auth)/layout.tsx
index d07d16eb0..b5903f597 100644
--- a/apps/web/src/app/(auth)/layout.tsx
+++ b/apps/web/src/app/(auth)/layout.tsx
@@ -1,5 +1,4 @@
-import { auth } from "@superset/auth";
-import { headers } from "next/headers";
+import { auth } from "@clerk/nextjs/server";
import Image from "next/image";
import { redirect } from "next/navigation";
@@ -10,11 +9,10 @@ export default async function AuthLayout({
}: {
children: React.ReactNode;
}) {
- const session = await auth.api.getSession({
- headers: await headers(),
- });
+ const { userId } = await auth();
- if (session) {
+ // Redirect authenticated users to home
+ if (userId) {
redirect("/");
}
diff --git a/apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
index c05643f6a..4e0c9c51b 100644
--- a/apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
+++ b/apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
@@ -1,46 +1,56 @@
"use client";
-import { authClient } from "@superset/auth/client";
+import { useSignIn } from "@clerk/nextjs";
import { Button } from "@superset/ui/button";
import Link from "next/link";
import { useState } from "react";
import { FaGithub } from "react-icons/fa";
import { FcGoogle } from "react-icons/fc";
+
import { env } from "@/env";
export default function SignInPage() {
+ const { signIn, isLoaded } = useSignIn();
const [isLoadingGoogle, setIsLoadingGoogle] = useState(false);
const [isLoadingGithub, setIsLoadingGithub] = useState(false);
const [error, setError] = useState(null);
const signInWithGoogle = async () => {
+ if (!isLoaded) return;
+
setIsLoadingGoogle(true);
setError(null);
try {
- await authClient.signIn.social({
- provider: "google",
- callbackURL: env.NEXT_PUBLIC_WEB_URL,
+ await signIn.authenticateWithRedirect({
+ strategy: "oauth_google",
+ redirectUrl: "/sso-callback",
+ redirectUrlComplete: "/",
});
} catch (err) {
console.error("Sign in failed:", err);
setError("Failed to sign in. Please try again.");
+ } finally {
setIsLoadingGoogle(false);
}
};
const signInWithGithub = async () => {
+ if (!isLoaded) return;
+
setIsLoadingGithub(true);
setError(null);
try {
- await authClient.signIn.social({
- provider: "github",
- callbackURL: env.NEXT_PUBLIC_WEB_URL,
+ await signIn.authenticateWithRedirect({
+ strategy: "oauth_github",
+ redirectUrl: "/sso-callback",
+ redirectUrlComplete: "/",
});
} catch (err) {
console.error("Sign in failed:", err);
setError("Failed to sign in. Please try again.");
+ } finally {
setIsLoadingGithub(false);
}
};
@@ -61,7 +71,7 @@ export default function SignInPage() {
)}
@@ -70,7 +80,7 @@ export default function SignInPage() {
diff --git a/apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx b/apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx
index dd9b579cd..35b9fdeb7 100644
--- a/apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx
+++ b/apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx
@@ -1,46 +1,56 @@
"use client";
-import { authClient } from "@superset/auth/client";
+import { useSignUp } from "@clerk/nextjs";
import { Button } from "@superset/ui/button";
import Link from "next/link";
import { useState } from "react";
import { FaGithub } from "react-icons/fa";
import { FcGoogle } from "react-icons/fc";
+
import { env } from "@/env";
export default function SignUpPage() {
+ const { signUp, isLoaded } = useSignUp();
const [isLoadingGoogle, setIsLoadingGoogle] = useState(false);
const [isLoadingGithub, setIsLoadingGithub] = useState(false);
const [error, setError] = useState(null);
const signUpWithGoogle = async () => {
+ if (!isLoaded) return;
+
setIsLoadingGoogle(true);
setError(null);
try {
- await authClient.signIn.social({
- provider: "google",
- callbackURL: env.NEXT_PUBLIC_WEB_URL,
+ await signUp.authenticateWithRedirect({
+ strategy: "oauth_google",
+ redirectUrl: "/sso-callback",
+ redirectUrlComplete: "/",
});
} catch (err) {
console.error("Sign up failed:", err);
setError("Failed to sign up. Please try again.");
+ } finally {
setIsLoadingGoogle(false);
}
};
const signUpWithGithub = async () => {
+ if (!isLoaded) return;
+
setIsLoadingGithub(true);
setError(null);
try {
- await authClient.signIn.social({
- provider: "github",
- callbackURL: env.NEXT_PUBLIC_WEB_URL,
+ await signUp.authenticateWithRedirect({
+ strategy: "oauth_github",
+ redirectUrl: "/sso-callback",
+ redirectUrlComplete: "/",
});
} catch (err) {
console.error("Sign up failed:", err);
setError("Failed to sign up. Please try again.");
+ } finally {
setIsLoadingGithub(false);
}
};
@@ -63,7 +73,7 @@ export default function SignUpPage() {
)}
@@ -72,7 +82,7 @@ export default function SignUpPage() {
diff --git a/apps/web/src/app/(auth)/sso-callback/page.tsx b/apps/web/src/app/(auth)/sso-callback/page.tsx
new file mode 100644
index 000000000..033b8ecc8
--- /dev/null
+++ b/apps/web/src/app/(auth)/sso-callback/page.tsx
@@ -0,0 +1,5 @@
+import { AuthenticateWithRedirectCallback } from "@clerk/nextjs";
+
+export default function SSOCallbackPage() {
+ return ;
+}
diff --git a/apps/web/src/app/(dashboard)/components/Header/Header.tsx b/apps/web/src/app/(dashboard)/components/Header/Header.tsx
index df03b021a..47dd869a3 100644
--- a/apps/web/src/app/(dashboard)/components/Header/Header.tsx
+++ b/apps/web/src/app/(dashboard)/components/Header/Header.tsx
@@ -1,6 +1,6 @@
"use client";
-import { authClient } from "@superset/auth/client";
+import { SignOutButton, useUser } from "@clerk/nextjs";
import { getInitials } from "@superset/shared/names";
import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar";
import {
@@ -12,19 +12,14 @@ import {
import { LogOut } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
-import { useRouter } from "next/navigation";
export function Header() {
- const { data: session } = authClient.useSession();
- const router = useRouter();
+ const { user } = useUser();
- const user = session?.user;
- const initials = getInitials(user?.name, user?.email);
-
- const handleSignOut = async () => {
- await authClient.signOut();
- router.push("/sign-in");
- };
+ const initials = getInitials(
+ user?.fullName,
+ user?.primaryEmailAddress?.emailAddress,
+ );
return (
@@ -45,22 +40,18 @@ export function Header() {
className="cursor-pointer rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
-
+
{initials}
-
-
- Logout
-
+
+
+
+ Logout
+
+
diff --git a/apps/web/src/app/(dashboard)/layout.tsx b/apps/web/src/app/(dashboard)/layout.tsx
index 2ec1eaeb8..a20ca12c3 100644
--- a/apps/web/src/app/(dashboard)/layout.tsx
+++ b/apps/web/src/app/(dashboard)/layout.tsx
@@ -1,7 +1,3 @@
-import { auth } from "@superset/auth";
-import { headers } from "next/headers";
-import { redirect } from "next/navigation";
-
import { api } from "@/trpc/server";
import { Footer } from "./components/Footer";
import { Header } from "./components/Header";
@@ -12,14 +8,6 @@ export default async function DashboardLayout({
}: {
children: React.ReactNode;
}) {
- const session = await auth.api.getSession({
- headers: await headers(),
- });
-
- if (!session) {
- redirect("/sign-in");
- }
-
const trpc = await api();
const organization = await trpc.user.myOrganization.query();
const displayName = organization?.name ?? "Superset";
diff --git a/apps/web/src/app/api/auth/desktop/github/route.ts b/apps/web/src/app/api/auth/desktop/github/route.ts
new file mode 100644
index 000000000..0cdc4819c
--- /dev/null
+++ b/apps/web/src/app/api/auth/desktop/github/route.ts
@@ -0,0 +1,81 @@
+import { redirect } from "next/navigation";
+import { env } from "@/env";
+
+export async function GET(request: Request) {
+ const url = new URL(request.url);
+ const code = url.searchParams.get("code");
+ const state = url.searchParams.get("state");
+ const error = url.searchParams.get("error");
+ const errorDescription = url.searchParams.get("error_description");
+
+ if (error) {
+ const errorUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ errorUrl.searchParams.set("error", errorDescription || error);
+ redirect(errorUrl.toString());
+ }
+
+ if (!code || !state) {
+ const errorUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ errorUrl.searchParams.set("error", "Missing authentication parameters");
+ redirect(errorUrl.toString());
+ }
+
+ let tokenData: {
+ accessToken: string;
+ accessTokenExpiresAt: number;
+ refreshToken: string;
+ refreshTokenExpiresAt: number;
+ } | null = null;
+ let exchangeError: string | null = null;
+
+ try {
+ const response = await fetch(
+ `${env.NEXT_PUBLIC_API_URL}/api/auth/desktop/github`,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ code,
+ redirectUri: `${env.NEXT_PUBLIC_WEB_URL}/api/auth/desktop/github`,
+ }),
+ },
+ );
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ exchangeError = errorData.error || "Failed to complete sign in";
+ } else {
+ tokenData = (await response.json()) as {
+ accessToken: string;
+ accessTokenExpiresAt: number;
+ refreshToken: string;
+ refreshTokenExpiresAt: number;
+ };
+ }
+ } catch (err) {
+ console.error("[api/auth/desktop/github] Error:", err);
+ exchangeError = "An unexpected error occurred";
+ }
+
+ if (exchangeError || !tokenData) {
+ const errorUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ errorUrl.searchParams.set("error", exchangeError || "Failed to sign in");
+ redirect(errorUrl.toString());
+ }
+
+ const successUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ successUrl.searchParams.set("accessToken", tokenData.accessToken);
+ successUrl.searchParams.set(
+ "accessTokenExpiresAt",
+ tokenData.accessTokenExpiresAt.toString(),
+ );
+ successUrl.searchParams.set("refreshToken", tokenData.refreshToken);
+ successUrl.searchParams.set(
+ "refreshTokenExpiresAt",
+ tokenData.refreshTokenExpiresAt.toString(),
+ );
+ successUrl.searchParams.set("state", state);
+ redirect(successUrl.toString());
+}
diff --git a/apps/web/src/app/api/auth/desktop/google/route.ts b/apps/web/src/app/api/auth/desktop/google/route.ts
new file mode 100644
index 000000000..affa5d6d8
--- /dev/null
+++ b/apps/web/src/app/api/auth/desktop/google/route.ts
@@ -0,0 +1,81 @@
+import { redirect } from "next/navigation";
+import { env } from "@/env";
+
+export async function GET(request: Request) {
+ const url = new URL(request.url);
+ const code = url.searchParams.get("code");
+ const state = url.searchParams.get("state");
+ const error = url.searchParams.get("error");
+ const errorDescription = url.searchParams.get("error_description");
+
+ if (error) {
+ const errorUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ errorUrl.searchParams.set("error", errorDescription || error);
+ redirect(errorUrl.toString());
+ }
+
+ if (!code || !state) {
+ const errorUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ errorUrl.searchParams.set("error", "Missing authentication parameters");
+ redirect(errorUrl.toString());
+ }
+
+ let tokenData: {
+ accessToken: string;
+ accessTokenExpiresAt: number;
+ refreshToken: string;
+ refreshTokenExpiresAt: number;
+ } | null = null;
+ let exchangeError: string | null = null;
+
+ try {
+ const response = await fetch(
+ `${env.NEXT_PUBLIC_API_URL}/api/auth/desktop/google`,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ code,
+ redirectUri: `${env.NEXT_PUBLIC_WEB_URL}/api/auth/desktop/google`,
+ }),
+ },
+ );
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ exchangeError = errorData.error || "Failed to complete sign in";
+ } else {
+ tokenData = (await response.json()) as {
+ accessToken: string;
+ accessTokenExpiresAt: number;
+ refreshToken: string;
+ refreshTokenExpiresAt: number;
+ };
+ }
+ } catch (err) {
+ console.error("[api/auth/desktop/google] Error:", err);
+ exchangeError = "An unexpected error occurred";
+ }
+
+ if (exchangeError || !tokenData) {
+ const errorUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ errorUrl.searchParams.set("error", exchangeError || "Failed to sign in");
+ redirect(errorUrl.toString());
+ }
+
+ const successUrl = new URL("/auth/desktop/success", env.NEXT_PUBLIC_WEB_URL);
+ successUrl.searchParams.set("accessToken", tokenData.accessToken);
+ successUrl.searchParams.set(
+ "accessTokenExpiresAt",
+ tokenData.accessTokenExpiresAt.toString(),
+ );
+ successUrl.searchParams.set("refreshToken", tokenData.refreshToken);
+ successUrl.searchParams.set(
+ "refreshTokenExpiresAt",
+ tokenData.refreshTokenExpiresAt.toString(),
+ );
+ successUrl.searchParams.set("state", state);
+ redirect(successUrl.toString());
+}
diff --git a/apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsx b/apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsx
new file mode 100644
index 000000000..8e84163d1
--- /dev/null
+++ b/apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsx
@@ -0,0 +1,96 @@
+"use client";
+
+import Image from "next/image";
+import Link from "next/link";
+import { useSearchParams } from "next/navigation";
+import { useCallback, useEffect, useState } from "react";
+
+const DESKTOP_PROTOCOL =
+ process.env.NODE_ENV === "development" ? "superset-dev" : "superset";
+
+export function DesktopAuthSuccess() {
+ const searchParams = useSearchParams();
+ const accessToken = searchParams.get("accessToken");
+ const accessTokenExpiresAt = searchParams.get("accessTokenExpiresAt");
+ const refreshToken = searchParams.get("refreshToken");
+ const refreshTokenExpiresAt = searchParams.get("refreshTokenExpiresAt");
+ const state = searchParams.get("state");
+ const error = searchParams.get("error");
+
+ const [hasAttempted, setHasAttempted] = useState(false);
+
+ const hasAllTokens =
+ accessToken &&
+ accessTokenExpiresAt &&
+ refreshToken &&
+ refreshTokenExpiresAt &&
+ state;
+
+ const desktopUrl = hasAllTokens
+ ? `${DESKTOP_PROTOCOL}://auth/callback?accessToken=${encodeURIComponent(accessToken)}&accessTokenExpiresAt=${encodeURIComponent(accessTokenExpiresAt)}&refreshToken=${encodeURIComponent(refreshToken)}&refreshTokenExpiresAt=${encodeURIComponent(refreshTokenExpiresAt)}&state=${encodeURIComponent(state)}`
+ : null;
+
+ const openDesktopApp = useCallback(() => {
+ if (!desktopUrl) return;
+ window.location.href = desktopUrl;
+ }, [desktopUrl]);
+
+ useEffect(() => {
+ if (error || !desktopUrl || hasAttempted) return;
+ setHasAttempted(true);
+ openDesktopApp();
+ }, [error, desktopUrl, hasAttempted, openDesktopApp]);
+
+ if (error) {
+ return (
+
+
+
Authentication failed
+
{error}
+
+ );
+ }
+
+ if (!hasAllTokens) {
+ return (
+
+
+
Invalid request
+
+ Missing authentication parameters. Please try again.
+
+
+ );
+ }
+
+ return (
+
+
+
+ Redirecting to the desktop app...
+
+
+ {desktopUrl && (
+
+ If you weren't redirected, click here.
+
+ )}
+
+
+ );
+}
diff --git a/apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.ts b/apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.ts
new file mode 100644
index 000000000..94e4dc63f
--- /dev/null
+++ b/apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.ts
@@ -0,0 +1 @@
+export { DesktopAuthSuccess } from "./DesktopAuthSuccess";
diff --git a/apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx b/apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx
deleted file mode 100644
index 678b3ae48..000000000
--- a/apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-"use client";
-
-import Image from "next/image";
-import Link from "next/link";
-import { useEffect } from "react";
-
-export function DesktopRedirect({ url }: { url: string }) {
- useEffect(() => {
- window.location.href = url;
- }, [url]);
-
- return (
-
-
-
- Redirecting to desktop app...
-
-
- Click here if not redirected
-
-
- );
-}
diff --git a/apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts b/apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
deleted file mode 100644
index 9f8d53871..000000000
--- a/apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { DesktopRedirect } from "./DesktopRedirect";
diff --git a/apps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsx b/apps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsx
new file mode 100644
index 000000000..b99538a47
--- /dev/null
+++ b/apps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsx
@@ -0,0 +1,10 @@
+import Image from "next/image";
+
+export function LoadingFallback() {
+ return (
+
+ );
+}
diff --git a/apps/web/src/app/auth/desktop/success/components/LoadingFallback/index.ts b/apps/web/src/app/auth/desktop/success/components/LoadingFallback/index.ts
new file mode 100644
index 000000000..ac26b6d3b
--- /dev/null
+++ b/apps/web/src/app/auth/desktop/success/components/LoadingFallback/index.ts
@@ -0,0 +1 @@
+export { LoadingFallback } from "./LoadingFallback";
diff --git a/apps/web/src/app/auth/desktop/success/page.tsx b/apps/web/src/app/auth/desktop/success/page.tsx
index 7f0780c81..595722bf5 100644
--- a/apps/web/src/app/auth/desktop/success/page.tsx
+++ b/apps/web/src/app/auth/desktop/success/page.tsx
@@ -1,89 +1,16 @@
-import { auth } from "@superset/auth";
-import { db } from "@superset/db/client";
-import { sessions } from "@superset/db/schema/auth";
-import { headers } from "next/headers";
+"use client";
-import { DesktopRedirect } from "./components/DesktopRedirect";
+import { Suspense } from "react";
-export default async function DesktopSuccessPage({
- searchParams,
-}: {
- searchParams: Promise<{ desktop_state?: string }>;
-}) {
- const { desktop_state: state } = await searchParams;
-
- if (!state) {
- return (
-
-
Missing auth state
-
- Please try signing in again from the desktop app.
-
-
- );
- }
-
- // Get session from Better Auth
- let session: Awaited> | null = null;
- try {
- session = await auth.api.getSession({ headers: await headers() });
- } catch (error) {
- console.error("Failed to get session for desktop auth:", error);
- return (
-
-
Authentication failed
-
- Please try signing in again from the desktop app.
-
-
- );
- }
-
- if (!session) {
- return (
-
-
Authentication failed
-
- Please try signing in again from the desktop app.
-
-
- );
- }
-
- // Create a separate session for the desktop app instead of reusing the browser session
- // This ensures desktop and web have independent sessions with separate activeOrganizationId
- const headersObj = await headers();
- const userAgent = headersObj.get("user-agent") || "Superset Desktop App";
- const ipAddress =
- headersObj.get("x-forwarded-for")?.split(",")[0] ||
- headersObj.get("x-real-ip") ||
- undefined;
-
- // Generate a unique session token for the desktop app
- const crypto = await import("node:crypto");
- const token = crypto.randomBytes(32).toString("base64url");
- const now = new Date();
- const expiresAt = new Date(
- Date.now() + 60 * 60 * 24 * 30 * 1000, // 30 days (matching auth config)
- );
-
- // Create a new session record in the database
- await db.insert(sessions).values({
- token,
- userId: session.user.id,
- expiresAt,
- ipAddress,
- userAgent,
- activeOrganizationId: session.session.activeOrganizationId,
- updatedAt: now,
- });
- const protocol =
- process.env.NODE_ENV === "development" ? "superset-dev" : "superset";
- const desktopUrl = `${protocol}://auth/callback?token=${encodeURIComponent(token)}&expiresAt=${encodeURIComponent(expiresAt.toISOString())}&state=${encodeURIComponent(state)}`;
+import { DesktopAuthSuccess } from "./components/DesktopAuthSuccess";
+import { LoadingFallback } from "./components/LoadingFallback";
+export default function Page() {
return (
-
+ }>
+
+
);
}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 48b154dfd..4bbd4d7ec 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -1,8 +1,11 @@
+import { ClerkProvider } from "@clerk/nextjs";
import { Toaster } from "@superset/ui/sonner";
import { cn } from "@superset/ui/utils";
import type { Metadata, Viewport } from "next";
import { IBM_Plex_Mono, Inter } from "next/font/google";
+import { env } from "@/env";
+
import "./globals.css";
import { Providers } from "./providers";
@@ -37,19 +40,28 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
-
-
-
- {children}
-
-
-
-
+
+
+
+
+ {children}
+
+
+
+
+
);
}
diff --git a/apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx b/apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
index ef0e3ba87..68f00c993 100644
--- a/apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
+++ b/apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
@@ -1,22 +1,27 @@
"use client";
-import { authClient } from "@superset/auth/client";
+import { useUser } from "@clerk/nextjs";
+import { useQuery } from "@tanstack/react-query";
import posthog from "posthog-js";
import { useEffect } from "react";
+import { useTRPC } from "../../trpc/react";
export function PostHogUserIdentifier() {
- const { data: session } = authClient.useSession();
+ const { isSignedIn } = useUser();
+ const trpc = useTRPC();
+
+ const { data: user } = useQuery({
+ ...trpc.user.me.queryOptions(),
+ enabled: isSignedIn,
+ });
useEffect(() => {
- if (session?.user) {
- posthog.identify(session.user.id, {
- email: session.user.email,
- name: session.user.name,
- });
- } else if (session === null) {
+ if (user) {
+ posthog.identify(user.id, { email: user.email, name: user.name });
+ } else if (isSignedIn === false) {
posthog.reset();
}
- }, [session]);
+ }, [user, isSignedIn]);
return null;
}
diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts
index 25a96d455..f82811e94 100644
--- a/apps/web/src/env.ts
+++ b/apps/web/src/env.ts
@@ -13,6 +13,8 @@ export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
DATABASE_URL_UNPOOLED: z.string().url(),
+ CLERK_SECRET_KEY: z.string(),
+ DESKTOP_AUTH_SECRET: z.string().min(32),
SENTRY_AUTH_TOKEN: z.string().optional(),
},
@@ -21,6 +23,8 @@ export const env = createEnv({
NEXT_PUBLIC_WEB_URL: z.string().url(),
NEXT_PUBLIC_MARKETING_URL: z.string().url(),
NEXT_PUBLIC_DOCS_URL: z.string().url(),
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string(),
+ NEXT_PUBLIC_COOKIE_DOMAIN: z.string(),
NEXT_PUBLIC_POSTHOG_KEY: z.string(),
NEXT_PUBLIC_POSTHOG_HOST: z.string().url(),
NEXT_PUBLIC_SENTRY_DSN_WEB: z.string().optional(),
@@ -35,6 +39,9 @@ export const env = createEnv({
NEXT_PUBLIC_WEB_URL: process.env.NEXT_PUBLIC_WEB_URL,
NEXT_PUBLIC_MARKETING_URL: process.env.NEXT_PUBLIC_MARKETING_URL,
NEXT_PUBLIC_DOCS_URL: process.env.NEXT_PUBLIC_DOCS_URL,
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:
+ process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
+ NEXT_PUBLIC_COOKIE_DOMAIN: process.env.NEXT_PUBLIC_COOKIE_DOMAIN,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
NEXT_PUBLIC_SENTRY_DSN_WEB: process.env.NEXT_PUBLIC_SENTRY_DSN_WEB,
diff --git a/apps/web/src/proxy.ts b/apps/web/src/proxy.ts
index 3f4158f9c..e8965f1fd 100644
--- a/apps/web/src/proxy.ts
+++ b/apps/web/src/proxy.ts
@@ -1,42 +1,39 @@
-import { auth } from "@superset/auth";
-import { headers } from "next/headers";
-import { type NextRequest, NextResponse } from "next/server";
-
-const publicRoutes = [
- "/sign-in",
- "/sign-up",
- "/auth/desktop",
- "/api/auth/desktop",
-];
-
-function isPublicRoute(pathname: string): boolean {
- return publicRoutes.some((route) => pathname.startsWith(route));
-}
-
-export default async function proxy(req: NextRequest) {
- const session = await auth.api.getSession({
- headers: await headers(),
- });
-
- const pathname = req.nextUrl.pathname;
-
+import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+const isPublicRoute = createRouteMatcher([
+ "/sign-in(.*)",
+ "/sign-up(.*)",
+ "/sso-callback(.*)",
+ "/auth/desktop(.*)",
+ "/api/auth/desktop(.*)",
+ "/ingest(.*)",
+ "/monitoring(.*)",
+]);
+
+export default clerkMiddleware(async (auth, req) => {
+ const { userId } = await auth();
+
+ // Redirect authenticated users away from auth pages
if (
- session &&
- (pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up"))
+ userId &&
+ (req.nextUrl.pathname.startsWith("/sign-in") ||
+ req.nextUrl.pathname.startsWith("/sign-up"))
) {
return NextResponse.redirect(new URL("/", req.url));
}
- if (!session && !isPublicRoute(pathname)) {
+ // Redirect unauthenticated users to sign-in
+ if (!userId && !isPublicRoute(req)) {
return NextResponse.redirect(new URL("/sign-in", req.url));
}
return NextResponse.next();
-}
+});
export const config = {
matcher: [
- "/((?!_next|ingest|monitoring|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
+ "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
"/(api|trpc)(.*)",
],
};
diff --git a/apps/web/src/trpc/react.tsx b/apps/web/src/trpc/react.tsx
index 78f7c07f9..95db3b924 100644
--- a/apps/web/src/trpc/react.tsx
+++ b/apps/web/src/trpc/react.tsx
@@ -1,5 +1,6 @@
"use client";
+import { useAuth } from "@clerk/nextjs";
import type { AppRouter } from "@superset/trpc";
import type { QueryClient } from "@tanstack/react-query";
import { QueryClientProvider } from "@tanstack/react-query";
@@ -32,6 +33,7 @@ export type UseTRPC = typeof useTRPC;
export function TRPCReactProvider(props: { children: React.ReactNode }) {
const queryClient = getQueryClient();
+ const { getToken } = useAuth();
const [trpcClient] = useState(() =>
createTRPCClient({
@@ -44,11 +46,12 @@ export function TRPCReactProvider(props: { children: React.ReactNode }) {
httpBatchStreamLink({
transformer: SuperJSON,
url: `${env.NEXT_PUBLIC_API_URL}/api/trpc`,
- headers() {
- return { "x-trpc-source": "nextjs-react" };
- },
- fetch(url, options) {
- return fetch(url, { ...options, credentials: "include" });
+ async headers() {
+ const token = await getToken();
+ return {
+ "x-trpc-source": "nextjs-react",
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ };
},
}),
],
diff --git a/biome.jsonc b/biome.jsonc
index e779f28b1..ce8290bd6 100644
--- a/biome.jsonc
+++ b/biome.jsonc
@@ -1,5 +1,5 @@
{
- "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
diff --git a/bun.lock b/bun.lock
index fc9a3d22d..cbb3e7cbc 100644
--- a/bun.lock
+++ b/bun.lock
@@ -14,8 +14,8 @@
"name": "@superset/admin",
"version": "0.1.0",
"dependencies": {
+ "@clerk/nextjs": "^6.36.2",
"@sentry/nextjs": "^10.32.1",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/trpc": "workspace:*",
@@ -26,7 +26,6 @@
"@trpc/client": "^11.7.1",
"@trpc/server": "^11.7.1",
"@trpc/tanstack-react-query": "^11.7.1",
- "better-auth": "^1.4.9",
"date-fns": "^4.1.0",
"drizzle-orm": "0.45.1",
"import-in-the-middle": "2.0.1",
@@ -58,10 +57,11 @@
"name": "@superset/api",
"version": "0.1.0",
"dependencies": {
+ "@clerk/backend": "^2.27.0",
+ "@clerk/nextjs": "^6.36.2",
"@electric-sql/client": "^1.3.1",
"@linear/sdk": "^68.1.0",
"@sentry/nextjs": "^10.32.1",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/trpc": "workspace:*",
@@ -69,7 +69,6 @@
"@trpc/server": "^11.7.1",
"@upstash/qstash": "^2.8.4",
"@vercel/blob": "^2.0.0",
- "better-auth": "^1.4.9",
"drizzle-orm": "0.45.1",
"import-in-the-middle": "2.0.1",
"jose": "^6.1.3",
@@ -131,7 +130,6 @@
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.8",
"@sentry/electron": "^7.5.0",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/local-db": "workspace:*",
"@superset/shared": "workspace:*",
@@ -192,7 +190,6 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^19.2.3",
- "react-hotkeys-hook": "^5.2.1",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"react-mosaic-component": "^6.1.1",
@@ -232,7 +229,6 @@
"bun-types": "^1.3.1",
"code-inspector-plugin": "^1.2.2",
"cross-env": "^10.0.0",
- "drizzle-kit": "0.31.8",
"electron": "39.1.2",
"electron-builder": "^26.0.12",
"electron-extension-installer": "^2.0.0",
@@ -287,10 +283,10 @@
"name": "@superset/marketing",
"version": "0.1.0",
"dependencies": {
+ "@clerk/nextjs": "^6.36.2",
"@react-three/drei": "^10.7.6",
"@react-three/fiber": "^9.4.0",
"@sentry/nextjs": "^10.32.1",
- "@superset/auth": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/ui": "workspace:*",
"@t3-oss/env-nextjs": "^0.13.8",
@@ -328,8 +324,8 @@
"name": "@superset/web",
"version": "0.1.0",
"dependencies": {
+ "@clerk/nextjs": "^6.36.2",
"@sentry/nextjs": "^10.32.1",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/trpc": "workspace:*",
@@ -341,7 +337,6 @@
"@trpc/server": "^11.7.1",
"@trpc/tanstack-react-query": "^11.7.1",
"@uiw/react-md-editor": "^4.0.11",
- "better-auth": "^1.4.9",
"framer-motion": "^12.23.26",
"geist": "^1.5.1",
"import-in-the-middle": "2.0.1",
@@ -370,24 +365,6 @@
"typescript": "^5.9.3",
},
},
- "packages/auth": {
- "name": "@superset/auth",
- "version": "0.1.0",
- "dependencies": {
- "@superset/db": "workspace:*",
- "@t3-oss/env-core": "^0.13.8",
- "@t3-oss/env-nextjs": "^0.13.8",
- "better-auth": "^1.4.9",
- "dotenv": "^17.2.3",
- "drizzle-orm": "0.45.1",
- "zod": "^4.1.13",
- },
- "devDependencies": {
- "@superset/typescript": "workspace:*",
- "@types/node": "^24.9.1",
- "typescript": "^5.9.3",
- },
- },
"packages/db": {
"name": "@superset/db",
"version": "0.1.0",
@@ -436,8 +413,8 @@
"name": "@superset/trpc",
"version": "0.1.0",
"dependencies": {
+ "@clerk/backend": "^2.27.0",
"@linear/sdk": "^68.1.0",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@t3-oss/env-core": "^0.13.8",
@@ -534,13 +511,13 @@
"packages": {
"7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="],
- "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BwV7DU/lAm3Xn6iyyvZdWgVxgLu3SNXzl5y57gMvkW4nGhAOV5269IrJzQwGt03bb107sa6H6uJwWxc77zXoGA=="],
+ "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.24", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-mflk80YF8hj8vrF9e1IHhovGKC1ubX+sY88pesSk3pUiXfH5VPO8dgzNnxjwsqsCZrnkHcztxS5cSl4TzSiEuA=="],
- "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
+ "@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="],
- "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
+ "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
- "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-mkOh+Wwawzuf5wa30bvc4nA+Qb6DIrGWgBhRR/Pw4T9nsgYait8izvXkNyU78D6Wcu3Z+KUdwCmLCxlWjEotYA=="],
+ "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-jsElTJ0sQ4wHRz+C45tfect76BwbTbgkgKByOzpCN9xG61N5V6u/glvg1CsNJhq2xJIFpKHSwG3D2wPPuEYOrQ=="],
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
@@ -592,31 +569,23 @@
"@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
- "@better-auth/core": ["@better-auth/core@1.4.9", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.1.12" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.7", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-JT2q4NDkQzN22KclUEoZ7qU6tl9HUTfK1ctg2oWlT87SEagkwJcnrUwS9VznL+u9ziOIfY27P0f7/jSnmvLcoQ=="],
-
- "@better-auth/telemetry": ["@better-auth/telemetry@1.4.9", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.9" } }, "sha512-Tthy1/Gmx+pYlbvRQPBTKfVei8+pJwvH1NZp+5SbhwA6K2EXIaoonx/K6N/AXYs2aKUpyR4/gzqDesDjL7zd6A=="],
+ "@biomejs/biome": ["@biomejs/biome@2.3.10", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.10", "@biomejs/cli-darwin-x64": "2.3.10", "@biomejs/cli-linux-arm64": "2.3.10", "@biomejs/cli-linux-arm64-musl": "2.3.10", "@biomejs/cli-linux-x64": "2.3.10", "@biomejs/cli-linux-x64-musl": "2.3.10", "@biomejs/cli-win32-arm64": "2.3.10", "@biomejs/cli-win32-x64": "2.3.10" }, "bin": { "biome": "bin/biome" } }, "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ=="],
- "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
+ "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w=="],
- "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="],
+ "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg=="],
- "@biomejs/biome": ["@biomejs/biome@2.3.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.8", "@biomejs/cli-darwin-x64": "2.3.8", "@biomejs/cli-linux-arm64": "2.3.8", "@biomejs/cli-linux-arm64-musl": "2.3.8", "@biomejs/cli-linux-x64": "2.3.8", "@biomejs/cli-linux-x64-musl": "2.3.8", "@biomejs/cli-win32-arm64": "2.3.8", "@biomejs/cli-win32-x64": "2.3.8" }, "bin": { "biome": "bin/biome" } }, "sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA=="],
+ "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA=="],
- "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww=="],
+ "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A=="],
- "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-lUDQ03D7y/qEao7RgdjWVGCu+BLYadhKTm40HkpJIi6kn8LSv5PAwRlew/DmwP4YZ9ke9XXoTIQDO1vAnbRZlA=="],
+ "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.10", "", { "os": "linux", "cpu": "x64" }, "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw=="],
- "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-Uo1OJnIkJgSgF+USx970fsM/drtPcQ39I+JO+Fjsaa9ZdCN1oysQmy6oAGbyESlouz+rzEckLTF6DS7cWse95g=="],
+ "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g=="],
- "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-PShR4mM0sjksUMyxbyPNMxoKFPVF48fU8Qe8Sfx6w6F42verbwRLbz+QiKNiDPRJwUoMG1nPM50OBL3aOnTevA=="],
+ "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ=="],
- "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-QDPMD5bQz6qOVb3kiBui0zKZXASLo0NIQ9JVJio5RveBEFgDgsvJFUvZIbMbUZT3T00M/1wdzwWXk4GIh0KaAw=="],
-
- "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-YGLkqU91r1276uwSjiUD/xaVikdxgV1QpsicT0bIA1TaieM6E5ibMZeSyjQ/izBn4tKQthUSsVZacmoJfa3pDA=="],
-
- "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-H4IoCHvL1fXKDrTALeTKMiE7GGWFAraDwBYFquE/L/5r1927Te0mYIGseXi4F+lrrwhSWbSGt5qPFswNoBaCxg=="],
-
- "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.8", "", { "os": "win32", "cpu": "x64" }, "sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w=="],
+ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.10", "", { "os": "win32", "cpu": "x64" }, "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ=="],
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.1", "", {}, "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw=="],
@@ -630,17 +599,27 @@
"@chevrotain/utils": ["@chevrotain/utils@11.0.3", "", {}, "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="],
- "@code-inspector/core": ["@code-inspector/core@1.3.2", "", { "dependencies": { "@vue/compiler-dom": "^3.5.13", "chalk": "^4.1.1", "dotenv": "^16.1.4", "launch-ide": "1.3.0", "portfinder": "^1.0.28" } }, "sha512-6bwCW7AbX/gYaoiZPFqkHAP5f/C0nXj29AixqBUNl1sRrsWFPHHfiaWH8WPmjx9IXI/nln109GvMiFHUpm2Mvg=="],
+ "@clerk/backend": ["@clerk/backend@2.29.0", "", { "dependencies": { "@clerk/shared": "^3.41.1", "@clerk/types": "^4.101.9", "cookie": "1.0.2", "standardwebhooks": "^1.0.0", "tslib": "2.8.1" } }, "sha512-cw4CK6ZHgeFROirlIOawelqRBxZAyH6v3GPSYZEEzYAL0WWUHx7cMXzoQcTMruH7w6UM7s3Ox+uUcINESWkQPA=="],
+
+ "@clerk/clerk-react": ["@clerk/clerk-react@5.59.2", "", { "dependencies": { "@clerk/shared": "^3.41.1", "tslib": "2.8.1" }, "peerDependencies": { "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "sha512-vFZ4LWPenbNnui4GqGGkicH/3SL7KhS9egTMv/m0Dj/sS7mUgmLqAFpqWkhbzN8s8/rybuvJsMyIU7M0kx8+Cw=="],
+
+ "@clerk/nextjs": ["@clerk/nextjs@6.36.5", "", { "dependencies": { "@clerk/backend": "^2.29.0", "@clerk/clerk-react": "^5.59.2", "@clerk/shared": "^3.41.1", "@clerk/types": "^4.101.9", "server-only": "0.0.1", "tslib": "2.8.1" }, "peerDependencies": { "next": "^13.5.7 || ^14.2.25 || ^15.2.3 || ^16", "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "sha512-qHNNbxhAZMHanv47DKc08Xc+y0gbsoQBFVYA+WRzwii5OWOoWmLlydTGKaqukqNw9km9IN9b2KWSAvs1oklp2g=="],
- "@code-inspector/esbuild": ["@code-inspector/esbuild@1.3.2", "", { "dependencies": { "@code-inspector/core": "1.3.2" } }, "sha512-AlcMO+pfwvh+m9L7bOeonTjgeJKaUsfEJxJIjyqUZMjk+F+ujhxFUKtZZU7RUvuQLr4e/flPdN+yxvWKfymr+g=="],
+ "@clerk/shared": ["@clerk/shared@3.41.1", "", { "dependencies": { "csstype": "3.1.3", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.9.0", "swr": "2.3.4" }, "peerDependencies": { "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-BCbT7Xodk2rndA2nV/lW8X5LMNTvFP5UG2wNN9cYuAcTaI6hYZP18/z2zef2gG4xIrK7WAEjGVzHscikqNtzFQ=="],
- "@code-inspector/mako": ["@code-inspector/mako@1.3.2", "", { "dependencies": { "@code-inspector/core": "1.3.2" } }, "sha512-WAeCAruJ5N73jqPWQgnsqyLp4Fn9hrn5vHohxhqqXyjD+qqCUKZNLrZeuD5kyku8k51LRJWQUe6MblW3IWyUJA=="],
+ "@clerk/types": ["@clerk/types@4.101.9", "", { "dependencies": { "@clerk/shared": "^3.41.1" } }, "sha512-RO00JqqmkIoI1o0XCtvudjaLpqEoe8PRDHlLS1r/aNZazUQCO0TT6nZOx1F3X+QJDjqYVY7YmYl3mtO2QVEk1g=="],
- "@code-inspector/turbopack": ["@code-inspector/turbopack@1.3.2", "", { "dependencies": { "@code-inspector/core": "1.3.2", "@code-inspector/webpack": "1.3.2" } }, "sha512-e7HJIwgGInSHLpHv1ZdAaILnJsREiwDL5363Gs6H0ZB2JvdHvLTpYFc6wlPLrZG4fp53FwsnULFhHtXDrjVYRg=="],
+ "@code-inspector/core": ["@code-inspector/core@1.3.4", "", { "dependencies": { "@vue/compiler-dom": "^3.5.13", "chalk": "^4.1.1", "dotenv": "^16.1.4", "launch-ide": "1.3.1", "portfinder": "^1.0.28" } }, "sha512-AUFtDH/hngBHrNVwW1z44ogZkaGhfmFQJZjyQrSCp+mERVQqa4QNGQpRqiIEWVCwJ2e3GCuwxeAr/k48r1nscA=="],
- "@code-inspector/vite": ["@code-inspector/vite@1.3.2", "", { "dependencies": { "@code-inspector/core": "1.3.2", "chalk": "4.1.1" } }, "sha512-uwtZ2JTA7eh19LUWhwsngwhgn7S1K8y9cqHK4vu2tEDbmahiQkQM/XytzPrsdAPxGu4M7huBJoGKXn32Tjrvgg=="],
+ "@code-inspector/esbuild": ["@code-inspector/esbuild@1.3.4", "", { "dependencies": { "@code-inspector/core": "1.3.4" } }, "sha512-VVZLPnaUNtX4fm07bKBkdIn2t1H0jPzah3fHd/CtNO82mnT1H3hZCuvtlO4jQZnErWC1KYJUEDrEe4T5TA5giQ=="],
- "@code-inspector/webpack": ["@code-inspector/webpack@1.3.2", "", { "dependencies": { "@code-inspector/core": "1.3.2" } }, "sha512-P9jrU7Hpca4y+ukaHRVP7w1rOIcNTPQONb5uITkt/dHDmI6/KDW5FfydcDI6Jc8HFRHLkTVZsZyUcllItA2VVQ=="],
+ "@code-inspector/mako": ["@code-inspector/mako@1.3.4", "", { "dependencies": { "@code-inspector/core": "1.3.4" } }, "sha512-SvjZSfLXgiWDMmJ9+YfPqbE2WVbXCNPjREclEJfqkM2qS/oRPmHw/O81p5wh6dN48gzVadLKEOPtSE0+FURJgQ=="],
+
+ "@code-inspector/turbopack": ["@code-inspector/turbopack@1.3.4", "", { "dependencies": { "@code-inspector/core": "1.3.4", "@code-inspector/webpack": "1.3.4" } }, "sha512-zsv2ppMFedNZetrlN4PEW4B2vAheu3yUfrmSKfZlXEb8YT378sq+49+57aP/E1Q8cHRzowy4GItuPKwAy7TTVQ=="],
+
+ "@code-inspector/vite": ["@code-inspector/vite@1.3.4", "", { "dependencies": { "@code-inspector/core": "1.3.4", "chalk": "4.1.1" } }, "sha512-BcRnQFwt8yQ4CcbN7yPf/Vmon3yfS5lPpcH0QAcjD03r61if5fmix29f65Rf/WxzlVyBkk7148xaIVQ3iT2Yjg=="],
+
+ "@code-inspector/webpack": ["@code-inspector/webpack@1.3.4", "", { "dependencies": { "@code-inspector/core": "1.3.4" } }, "sha512-lqsDOSmKXgOYvlurWL4SHaNItNCNZDVbFJlroM4ECnOGt/iNUI0UwaFxn9U5NeKff7fnTmdH8Hrz2IaMR4FVTg=="],
"@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
@@ -660,8 +639,6 @@
"@electric-sql/client": ["@electric-sql/client@1.3.1", "", { "dependencies": { "@microsoft/fetch-event-source": "^2.0.1" }, "optionalDependencies": { "@rollup/rollup-darwin-arm64": "^4.18.1" } }, "sha512-Tr7jY60acKfxuODOzGcJYzEXtTJyiE8l237hMpCPHjflr+qSGYVZ0NkOnFgAE2xIPFgP1zn05nQb3+mkUu8qUQ=="],
- "@electric-sql/pglite": ["@electric-sql/pglite@0.3.14", "", {}, "sha512-3DB258dhqdsArOI1fIt7cb9RpUOgcDg5hXWVgVHAeqVQ/qxtFy605QKs4gx6mFq3jWsSPqDN8TgSEsqC3OfV9Q=="],
-
"@electron/asar": ["@electron/asar@3.2.18", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg=="],
"@electron/fuses": ["@electron/fuses@1.8.0", "", { "dependencies": { "chalk": "^4.1.1", "fs-extra": "^9.0.1", "minimist": "^1.2.5" }, "bin": { "electron-fuses": "dist/bin.js" } }, "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw=="],
@@ -892,27 +869,23 @@
"@neondatabase/serverless": ["@neondatabase/serverless@1.0.2", "", { "dependencies": { "@types/node": "^22.15.30", "@types/pg": "^8.8.0" } }, "sha512-I5sbpSIAHiB+b6UttofhrN/UJXII+4tZPAq1qugzwCwLIL8EZLV7F/JyHUrEIiGgQpEXzpnjlJ+zwcEhheGvCw=="],
- "@next/env": ["@next/env@16.0.10", "", {}, "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang=="],
-
- "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg=="],
-
- "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw=="],
+ "@next/env": ["@next/env@16.1.1", "", {}, "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA=="],
- "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw=="],
+ "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA=="],
- "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw=="],
+ "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw=="],
- "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA=="],
+ "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ=="],
- "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g=="],
+ "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg=="],
- "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg=="],
+ "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ=="],
- "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.10", "", { "os": "win32", "cpu": "x64" }, "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q=="],
+ "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA=="],
- "@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="],
+ "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA=="],
- "@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
+ "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -1102,13 +1075,13 @@
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
- "@react-aria/focus": ["@react-aria/focus@3.21.2", "", { "dependencies": { "@react-aria/interactions": "^3.25.6", "@react-aria/utils": "^3.31.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ=="],
+ "@react-aria/focus": ["@react-aria/focus@3.21.3", "", { "dependencies": { "@react-aria/interactions": "^3.26.0", "@react-aria/utils": "^3.32.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw=="],
- "@react-aria/interactions": ["@react-aria/interactions@3.25.6", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.31.0", "@react-stately/flags": "^3.1.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw=="],
+ "@react-aria/interactions": ["@react-aria/interactions@3.26.0", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.32.0", "@react-stately/flags": "^3.1.2", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q=="],
"@react-aria/ssr": ["@react-aria/ssr@3.9.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ=="],
- "@react-aria/utils": ["@react-aria/utils@3.31.0", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-stately/flags": "^3.1.2", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig=="],
+ "@react-aria/utils": ["@react-aria/utils@3.32.0", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-stately/flags": "^3.1.2", "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg=="],
"@react-dnd/asap": ["@react-dnd/asap@5.0.2", "", {}, "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A=="],
@@ -1118,11 +1091,11 @@
"@react-stately/flags": ["@react-stately/flags@3.1.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg=="],
- "@react-stately/utils": ["@react-stately/utils@3.10.8", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g=="],
+ "@react-stately/utils": ["@react-stately/utils@3.11.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw=="],
"@react-three/drei": ["@react-three/drei@10.7.7", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", "@use-gesture/react": "^10.3.1", "camera-controls": "^3.1.0", "cross-env": "^7.0.3", "detect-gpu": "^5.0.56", "glsl-noise": "^0.0.0", "hls.js": "^1.5.17", "maath": "^0.10.8", "meshline": "^3.3.1", "stats-gl": "^2.2.8", "stats.js": "^0.17.0", "suspend-react": "^0.1.3", "three-mesh-bvh": "^0.8.3", "three-stdlib": "^2.35.6", "troika-three-text": "^0.52.4", "tunnel-rat": "^0.1.2", "use-sync-external-store": "^1.4.0", "utility-types": "^3.11.0", "zustand": "^5.0.1" }, "peerDependencies": { "@react-three/fiber": "^9.0.0", "react": "^19", "react-dom": "^19", "three": ">=0.159" }, "optionalPeers": ["react-dom"] }, "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ=="],
- "@react-three/fiber": ["@react-three/fiber@9.4.2", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/react-reconciler": "^0.32.0", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-reconciler": "^0.31.0", "react-use-measure": "^2.1.7", "scheduler": "^0.25.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-H4B4+FDNHpvIb4FmphH4ubxOfX5bxmfOw0+3pkQwR9u9wFiyMS7wUDkNn0m4RqQuiLWeia9jfN1eBvtyAVGEog=="],
+ "@react-three/fiber": ["@react-three/fiber@9.5.0", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-use-measure": "^2.1.7", "scheduler": "^0.27.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA=="],
"@react-types/shared": ["@react-types/shared@3.32.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w=="],
@@ -1132,49 +1105,49 @@
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
- "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="],
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng=="],
- "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="],
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.54.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw=="],
- "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="],
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.54.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw=="],
- "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="],
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.54.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A=="],
- "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="],
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.54.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA=="],
- "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="],
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.54.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ=="],
- "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="],
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ=="],
- "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="],
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA=="],
- "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="],
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng=="],
- "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="],
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg=="],
- "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="],
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw=="],
- "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="],
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.54.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA=="],
- "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="],
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ=="],
- "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="],
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A=="],
- "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="],
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.54.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ=="],
- "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="],
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ=="],
- "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="],
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw=="],
- "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="],
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.54.0", "", { "os": "none", "cpu": "arm64" }, "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg=="],
- "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="],
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.54.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw=="],
- "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="],
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.54.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ=="],
- "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="],
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ=="],
- "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="],
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg=="],
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
@@ -1228,19 +1201,19 @@
"@sentry/webpack-plugin": ["@sentry/webpack-plugin@4.6.1", "", { "dependencies": { "@sentry/bundler-plugin-core": "4.6.1", "unplugin": "1.0.1", "uuid": "^9.0.0" }, "peerDependencies": { "webpack": ">=4.40.0" } }, "sha512-CJgT/t2pQWsPsMx9VJ86goU/orCQhL2HhDj5ZYBol6fPPoEGeTqKOPCnv/xsbCAfGSp1uHpyRLTA/Gx96u7VVA=="],
- "@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="],
+ "@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
- "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ=="],
+ "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg=="],
- "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg=="],
+ "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ=="],
- "@shikijs/langs": ["@shikijs/langs@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg=="],
+ "@shikijs/langs": ["@shikijs/langs@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA=="],
- "@shikijs/themes": ["@shikijs/themes@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A=="],
+ "@shikijs/themes": ["@shikijs/themes@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ=="],
- "@shikijs/twoslash": ["@shikijs/twoslash@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/types": "3.19.0", "twoslash": "^0.3.4" }, "peerDependencies": { "typescript": ">=5.5.0" } }, "sha512-DnkH4slSLPC7dJPhZ9Eofy1X/ZgXiWsvOl/ERK7799ZqXsJwtsq2e8RgHBQUX4Y2lf6aoMojirocLY0AbPF3Dg=="],
+ "@shikijs/twoslash": ["@shikijs/twoslash@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0", "twoslash": "^0.3.4" }, "peerDependencies": { "typescript": ">=5.5.0" } }, "sha512-fZz6vB9a0M8iuVF/ydIV4ToC09sbOh/TqxXZFWAh5J8bLiPsyQGtygKMDQ9L0Sdop3co0TIC/JsrLmsbmZwwsw=="],
- "@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="],
+ "@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
@@ -1248,7 +1221,9 @@
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
- "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
+ "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
+
+ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
@@ -1256,8 +1231,6 @@
"@superset/api": ["@superset/api@workspace:apps/api"],
- "@superset/auth": ["@superset/auth@workspace:packages/auth"],
-
"@superset/cli": ["@superset/cli@workspace:apps/cli"],
"@superset/db": ["@superset/db@workspace:packages/db"],
@@ -1284,9 +1257,9 @@
"@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="],
- "@t3-oss/env-core": ["@t3-oss/env-core@0.13.8", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-L1inmpzLQyYu4+Q1DyrXsGJYCXbtXjC4cICw1uAKv0ppYPQv656lhZPU91Qd1VS6SO/bou1/q5ufVzBGbNsUpw=="],
+ "@t3-oss/env-core": ["@t3-oss/env-core@0.13.10", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-NNFfdlJ+HmPHkLi2HKy7nwuat9SIYOxei9K10lO2YlcSObDILY7mHZNSHsieIM3A0/5OOzw/P/b+yLvPdaG52g=="],
- "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.8", "", { "dependencies": { "@t3-oss/env-core": "0.13.8" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-QmTLnsdQJ8BiQad2W2nvV6oUpH4oMZMqnFEjhVpzU0h3sI9hn8zb8crjWJ1Amq453mGZs6A4v4ihIeBFDOrLeQ=="],
+ "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.10", "", { "dependencies": { "@t3-oss/env-core": "0.13.10" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-JfSA2WXOnvcc/uMdp31paMsfbYhhdvLLRxlwvrnlPE9bwM/n0Z+Qb9xRv48nPpvfMhOrkrTYw1I5Yc06WIKBJQ=="],
"@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
@@ -1328,21 +1301,21 @@
"@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.1.1", "", {}, "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w=="],
- "@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="],
+ "@tanstack/query-core": ["@tanstack/query-core@5.90.16", "", {}, "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww=="],
- "@tanstack/query-devtools": ["@tanstack/query-devtools@5.91.1", "", {}, "sha512-l8bxjk6BMsCaVQH6NzQEE/bEgFy1hAs5qbgXl0xhzezlaQbPk6Mgz9BqEg2vTLPOHD8N4k+w/gdgCbEzecGyNg=="],
+ "@tanstack/query-devtools": ["@tanstack/query-devtools@5.92.0", "", {}, "sha512-N8D27KH1vEpVacvZgJL27xC6yPFUy0Zkezn5gnB3L3gRCxlDeSuiya7fKge8Y91uMTnC8aSxBQhcK6ocY7alpQ=="],
"@tanstack/react-db": ["@tanstack/react-db@0.1.60", "", { "dependencies": { "@tanstack/db": "0.5.16", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-Pz3pwH4vgRxlS/L3+BszINjNlIUXahJ6EqSEsYBMBT19G36GzTzyd5Qq98JaOXuu8V0dkiFWEoyEoTv3BiHcrg=="],
- "@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="],
+ "@tanstack/react-query": ["@tanstack/react-query@5.90.16", "", { "dependencies": { "@tanstack/query-core": "5.90.16" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ=="],
- "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.1", "", { "dependencies": { "@tanstack/query-devtools": "5.91.1" }, "peerDependencies": { "@tanstack/react-query": "^5.90.10", "react": "^18 || ^19" } }, "sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ=="],
+ "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.2", "", { "dependencies": { "@tanstack/query-devtools": "5.92.0" }, "peerDependencies": { "@tanstack/react-query": "^5.90.14", "react": "^18 || ^19" } }, "sha512-ZJ1503ay5fFeEYFUdo7LMNFzZryi6B0Cacrgr2h1JRkvikK1khgIq6Nq2EcblqEdIlgB/r7XDW8f8DQ89RuUgg=="],
- "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.13", "", { "dependencies": { "@tanstack/virtual-core": "3.13.13" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4o6oPMDvQv+9gMi8rE6gWmsOjtUZUYIJHv7EB+GblyYdi8U6OqLl8rhHWIUZSL1dUU2dPwTdTgybCKf9EjIrQg=="],
+ "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.14", "", { "dependencies": { "@tanstack/virtual-core": "3.13.14" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WG0d7mBD54eA7dgA3+sO5csS0B49QKqM6Gy5Rf31+Oq/LTKROQSao9m2N/vz1IqVragOKU5t5k1LAcqh/DfTxw=="],
"@tanstack/store": ["@tanstack/store@0.8.0", "", {}, "sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ=="],
- "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.13", "", {}, "sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA=="],
+ "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.14", "", {}, "sha512-b5Uvd8J2dc7ICeX9SRb/wkCxWk7pUwN214eEPAQsqrsktSKTCmyLxOQWSMgogBByXclZeAdgZ3k4o0fIYUIBqQ=="],
"@theguild/remark-mermaid": ["@theguild/remark-mermaid@0.3.0", "", { "dependencies": { "mermaid": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": "^18.2.0 || ^19.0.0" } }, "sha512-Fy1J4FSj8totuHsHFpaeWyWRaRSIvpzGTRoEfnNJc1JmLV9uV70sYE3zcT+Jj5Yw20Xq4iCsiT+3Ho49BBZcBQ=="],
@@ -1358,13 +1331,13 @@
"@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
- "@trpc/client": ["@trpc/client@11.7.2", "", { "peerDependencies": { "@trpc/server": "11.7.2", "typescript": ">=5.7.2" } }, "sha512-OQxqUMfpDvjcszo9dbnqWQXnW2L5IbrKSz2H7l8s+mVM3EvYw7ztQ/gjFIN3iy0NcamiQfd4eE6qjcb9Lm+63A=="],
+ "@trpc/client": ["@trpc/client@11.8.1", "", { "peerDependencies": { "@trpc/server": "11.8.1", "typescript": ">=5.7.2" } }, "sha512-L/SJFGanr9xGABmuDoeXR4xAdHJmsXsiF9OuH+apecJ+8sUITzVT1EPeqp0ebqA6lBhEl5pPfg3rngVhi/h60Q=="],
- "@trpc/react-query": ["@trpc/react-query@11.7.2", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.7.2", "@trpc/server": "11.7.2", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-IcLDMqx2mvlGRxkr0/m37TtPvRQ8nonITH3EwYv436x0Igx8eduR9z4tdgGBsjJY9e5W1G7cZ4zKCwrizSimFQ=="],
+ "@trpc/react-query": ["@trpc/react-query@11.8.1", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.8.1", "@trpc/server": "11.8.1", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-0Vu55ld/oINb4U6nIPPi7eZMhxUop6K+4QUK90RVsfSD5r+957sM80M4c8bjh/JBZUxMFv9JOhxxlWcrgHxHow=="],
- "@trpc/server": ["@trpc/server@11.7.2", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-AgB26PXY69sckherIhCacKLY49rxE2XP5h38vr/KMZTbLCL1p8IuIoKPjALTcugC2kbyQ7Lbqo2JDVfRSmPmfQ=="],
+ "@trpc/server": ["@trpc/server@11.8.1", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-P4rzZRpEL7zDFgjxK65IdyH0e41FMFfTkQkuq0BA5tKcr7E6v9/v38DEklCpoDN6sPiB1Sigy/PUEzHENhswDA=="],
- "@trpc/tanstack-react-query": ["@trpc/tanstack-react-query@11.7.2", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.7.2", "@trpc/server": "11.7.2", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-3XrY0b8lV0Fhj4Z2hVn1d1ZJzq2/stbc2F1e9Y6RrUWOfLmOKHlEVHYO1QfDGM6rqj66DkUj7eA593hAI0VTkQ=="],
+ "@trpc/tanstack-react-query": ["@trpc/tanstack-react-query@11.8.1", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.8.1", "@trpc/server": "11.8.1", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-0gBbRmRuU9/IVZgNFJuzY7k6ktnkmqjyP78PtcUBO7EgPlBu1y2MA2TdI5GT/VtNNOOhzkVeQpJqtt+0xQ8TOQ=="],
"@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="],
@@ -1500,7 +1473,7 @@
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
- "@types/node": ["@types/node@24.10.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-gqkrWUsS8hcm0r44yn7/xZeV1ERva/nLgrLxFRUGb7aoNMIJfZJ3AC261zDQuOAKC7MiXai1WCpYc48jAHoShQ=="],
+ "@types/node": ["@types/node@24.10.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg=="],
"@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="],
@@ -1522,7 +1495,7 @@
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
- "@types/react-reconciler": ["@types/react-reconciler@0.32.3", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA=="],
+ "@types/react-reconciler": ["@types/react-reconciler@0.28.9", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="],
"@types/react-syntax-highlighter": ["@types/react-syntax-highlighter@15.5.13", "", { "dependencies": { "@types/react": "*" } }, "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA=="],
@@ -1566,7 +1539,7 @@
"@upstash/qstash": ["@upstash/qstash@2.8.4", "", { "dependencies": { "crypto-js": ">=4.2.0", "jose": "^5.2.3", "neverthrow": "^7.0.1" } }, "sha512-iojHWUlRoC3M2e4XQ1NFEgC+7Orurrm5uIrPn5WilU7LvWQoocyjYBXR0VUalXkeMAmFyk4blF0EOYZY4igdIQ=="],
- "@upstash/redis": ["@upstash/redis@1.35.8", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-QqLpVCD9PCPE6hlRzOkz864nfijSdazxtyJLIy9ZeTh6kU2nBIKKfjT5HMHjIRD4BCm6TK1dbUT9pxhFjcvpng=="],
+ "@upstash/redis": ["@upstash/redis@1.36.0", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-9zN2UV9QJGPnXfWU3yZBLVQaqqENDh7g+Y4J2vJuSxBCi9FQ0aUOtaXlzuFhnsiZvCqM+eS27ic+tgmkWUsfOg=="],
"@use-gesture/core": ["@use-gesture/core@10.3.1", "", {}, "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw=="],
@@ -1580,11 +1553,11 @@
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.53", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ=="],
- "@vue/compiler-core": ["@vue/compiler-core@3.5.25", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.25", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw=="],
+ "@vue/compiler-core": ["@vue/compiler-core@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.26", "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w=="],
- "@vue/compiler-dom": ["@vue/compiler-dom@3.5.25", "", { "dependencies": { "@vue/compiler-core": "3.5.25", "@vue/shared": "3.5.25" } }, "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q=="],
+ "@vue/compiler-dom": ["@vue/compiler-dom@3.5.26", "", { "dependencies": { "@vue/compiler-core": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A=="],
- "@vue/shared": ["@vue/shared@3.5.25", "", {}, "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg=="],
+ "@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="],
"@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="],
@@ -1616,7 +1589,7 @@
"@webassemblyjs/wast-printer": ["@webassemblyjs/wast-printer@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw=="],
- "@webgpu/types": ["@webgpu/types@0.1.67", "", {}, "sha512-uk53+2ECGUkWoDFez/hymwpRfdgdIn6y1ref70fEecGMe5607f4sozNFgBk0oxlr7j2CRGWBEc3IBYMmFdGGTQ=="],
+ "@webgpu/types": ["@webgpu/types@0.1.68", "", {}, "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA=="],
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="],
@@ -1668,7 +1641,7 @@
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
- "ai": ["ai@5.0.112", "", { "dependencies": { "@ai-sdk/gateway": "2.0.21", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Y0dluYpe5wn81UkfHbZL78mH6CsceUfMiu4oPRaWZvjlmcoXSPdEAsPcYbOjvX8ZPvQc6m4kNZhkcEXmT2ln4w=="],
+ "ai": ["ai@5.0.117", "", { "dependencies": { "@ai-sdk/gateway": "2.0.24", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-uE6HNkdSwxbeHGKP/YbvapwD8fMOpj87wyfT9Z00pbzOh2fpnw5acak/4kzU00SX2vtI9K0uuy+9Tf9ytw5RwA=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
@@ -1724,14 +1697,10 @@
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
- "baseline-browser-mapping": ["baseline-browser-mapping@2.9.6", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg=="],
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.9.11", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ=="],
"bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="],
- "better-auth": ["better-auth@1.4.9", "", { "dependencies": { "@better-auth/core": "1.4.9", "@better-auth/telemetry": "1.4.9", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.7", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.12" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-usSdjuyTzZwIvM8fjF8YGhPncxV3MAg3dHUO9uPUnf0yklXUSYISiH1+imk6/Z+UBqsscyyPRnbIyjyK97p7YA=="],
-
- "better-call": ["better-call@1.1.7", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-6gaJe1bBIEgVebQu/7q9saahVzvBsGaByEnE8aDVncZEDiJO7sdNB28ot9I6iXSbR25egGmmZ6aIURXyQHRraQ=="],
-
"better-react-mathjax": ["better-react-mathjax@2.3.0", "", { "dependencies": { "mathjax-full": "^3.2.2" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-K0ceQC+jQmB+NLDogO5HCpqmYf18AU2FxDbLdduYgkHYWZApFggkHE4dIaXCV1NqeoscESYXXo1GSkY6fA295w=="],
"better-sqlite3": ["better-sqlite3@12.5.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg=="],
@@ -1766,7 +1735,7 @@
"builder-util-runtime": ["builder-util-runtime@9.3.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ=="],
- "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="],
+ "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
@@ -1788,7 +1757,7 @@
"camera-controls": ["camera-controls@3.1.2", "", { "peerDependencies": { "three": ">=0.126.1" } }, "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA=="],
- "caniuse-lite": ["caniuse-lite@1.0.30001760", "", {}, "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw=="],
+ "caniuse-lite": ["caniuse-lite@1.0.30001762", "", {}, "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
@@ -1852,7 +1821,7 @@
"code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
- "code-inspector-plugin": ["code-inspector-plugin@1.3.2", "", { "dependencies": { "@code-inspector/core": "1.3.2", "@code-inspector/esbuild": "1.3.2", "@code-inspector/mako": "1.3.2", "@code-inspector/turbopack": "1.3.2", "@code-inspector/vite": "1.3.2", "@code-inspector/webpack": "1.3.2", "chalk": "4.1.1" } }, "sha512-cnS4OlrHPI1xEj8H2efWVBsV9i8pJJYHHjZdjz/qSt6BVyJjvVkkTXI5Hl4I7vTgoAam1h4xTuEDkwisDQy2Xg=="],
+ "code-inspector-plugin": ["code-inspector-plugin@1.3.4", "", { "dependencies": { "@code-inspector/core": "1.3.4", "@code-inspector/esbuild": "1.3.4", "@code-inspector/mako": "1.3.4", "@code-inspector/turbopack": "1.3.4", "@code-inspector/vite": "1.3.4", "@code-inspector/webpack": "1.3.4", "chalk": "4.1.1" } }, "sha512-735rAAc655oSAMd/6+PIsjpgB5jwugDISom9WFwhNUbOuFHiL2PYwshMmfIhAtOgECl+7g6o50rBIIYwCEa8xg=="],
"collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
@@ -1886,7 +1855,7 @@
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
- "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
+ "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
@@ -2016,8 +1985,6 @@
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
- "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
-
"delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
@@ -2126,7 +2093,7 @@
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
- "es-toolkit": ["es-toolkit@1.42.0", "", {}, "sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA=="],
+ "es-toolkit": ["es-toolkit@1.43.0", "", {}, "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA=="],
"es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="],
@@ -2192,15 +2159,17 @@
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
- "fast-equals": ["fast-equals@5.3.3", "", {}, "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw=="],
+ "fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
+ "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="],
+
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
- "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
+ "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
"fault": ["fault@1.0.4", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="],
@@ -2414,7 +2383,7 @@
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
- "ink": ["ink@6.5.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.2.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^5.1.1", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^8.1.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-wF3j/DmkM8q5E+OtfdQhCRw8/0ahkc8CUTgEddxZzpEWPslu7YPL3t64MWRoI9m6upVGpfAg4ms2BBvxCdKRLQ=="],
+ "ink": ["ink@6.6.0", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.2.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^5.1.1", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^8.1.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ=="],
"ink-select-input": ["ink-select-input@6.2.0", "", { "dependencies": { "figures": "^6.1.0", "to-rotated": "^1.0.0" }, "peerDependencies": { "ink": ">=5.0.0", "react": ">=18.0.0" } }, "sha512-304fZXxkpYxJ9si5lxRCaX01GNlmPBgOZumXXRnPYbHW/iI31cgQynqk2tRypGLOF1cMIwPUzL2LSm6q4I5rQQ=="],
@@ -2510,6 +2479,8 @@
"js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="],
+ "js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="],
+
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
@@ -2540,11 +2511,9 @@
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
- "kysely": ["kysely@0.28.9", "", {}, "sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA=="],
-
"langium": ["langium@3.3.1", "", { "dependencies": { "chevrotain": "~11.0.3", "chevrotain-allstar": "~0.3.0", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.0.8" } }, "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w=="],
- "launch-ide": ["launch-ide@1.3.0", "", { "dependencies": { "chalk": "^4.1.1", "dotenv": "^16.1.4" } }, "sha512-pxiF+HVNMV0dDc6Z0q89RDmzMF9XmSGaOn4ueTegjMy3cUkezc3zrki5PCiz68zZIqAuhW7iwoWX7JO4Kn6B0A=="],
+ "launch-ide": ["launch-ide@1.3.1", "", { "dependencies": { "chalk": "^4.1.1", "dotenv": "^16.1.4" } }, "sha512-opTthrpkuhi1Y8yFn6TWUeycyiI1aiZpVuTV4HQFUfVut7nMYGr5nQ8heYHrRJH2KYISLVYwz+QFRNZxFlbQmA=="],
"layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="],
@@ -2588,7 +2557,7 @@
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
- "lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
+ "lodash-es": ["lodash-es@4.17.22", "", {}, "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q=="],
"lodash.chunk": ["lodash.chunk@4.2.0", "", {}, "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w=="],
@@ -2822,8 +2791,6 @@
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
- "nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="],
-
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
@@ -2832,7 +2799,7 @@
"neverthrow": ["neverthrow@7.2.0", "", {}, "sha512-iGBUfFB7yPczHHtA8dksKTJ9E8TESNTAx1UQWW6TzMF280vo9jdPYpLUXrMN1BCkPdHFdNG3fxOt2CUad8KhAw=="],
- "next": ["next@16.0.10", "", { "dependencies": { "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.10", "@next/swc-darwin-x64": "16.0.10", "@next/swc-linux-arm64-gnu": "16.0.10", "@next/swc-linux-arm64-musl": "16.0.10", "@next/swc-linux-x64-gnu": "16.0.10", "@next/swc-linux-x64-musl": "16.0.10", "@next/swc-win32-arm64-msvc": "16.0.10", "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA=="],
+ "next": ["next@16.1.1", "", { "dependencies": { "@next/env": "16.1.1", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.1", "@next/swc-darwin-x64": "16.1.1", "@next/swc-linux-arm64-gnu": "16.1.1", "@next/swc-linux-arm64-musl": "16.1.1", "@next/swc-linux-x64-gnu": "16.1.1", "@next/swc-linux-x64-musl": "16.1.1", "@next/swc-win32-arm64-msvc": "16.1.1", "@next/swc-win32-x64-msvc": "16.1.1", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w=="],
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
@@ -2972,7 +2939,7 @@
"postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
- "postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
+ "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
@@ -2980,13 +2947,13 @@
"posthog-js": ["posthog-js@1.310.1", "", { "dependencies": { "@posthog/core": "1.9.0", "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", "web-vitals": "^4.2.4" } }, "sha512-UkR6zzlWNtqHDXHJl2Yk062DOmZyVKTPL5mX4j4V+u3RiYbMHJe47+PpMMUsvK1R2e1r/m9uSlHaJMJRzyUjGg=="],
- "posthog-node": ["posthog-node@5.18.0", "", { "dependencies": { "@posthog/core": "1.9.0" } }, "sha512-SLBEs+sCThxzTGSSDEe97nZHuFFYh6DupObR1yQdvQND3CJh0ogZ0Sa1Vb+Tbrnf0cWbfBC9XNkm44yhaWf3aA=="],
+ "posthog-node": ["posthog-node@5.18.1", "", { "dependencies": { "@posthog/core": "1.9.0" } }, "sha512-Hi7cRqAlvuEitdiurXJFdMip+BxcwYoX66at5RErMVP91V+Ph9BspGiawC3mJx/4znjwUjF29kAhf8oZQ2uJ5Q=="],
"postject": ["postject@1.0.0-alpha.6", "", { "dependencies": { "commander": "^9.4.0" }, "bin": { "postject": "dist/cli.js" } }, "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A=="],
"potpack": ["potpack@1.0.2", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="],
- "preact": ["preact@10.28.0", "", {}, "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA=="],
+ "preact": ["preact@10.28.1", "", {}, "sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw=="],
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
@@ -3020,7 +2987,7 @@
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
- "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
+ "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
@@ -3042,7 +3009,7 @@
"react-compiler-runtime": ["react-compiler-runtime@19.1.0-rc.1-rc-af1b7da-20250421", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" } }, "sha512-Til/juI+Zfq+eYpGYn9lFxqW5RyJDs3ThOxmg0757aMrPpfx/Zb0SnGMVJhF3vw+bEQjJiD+xPFD3+kE0WbyeA=="],
- "react-day-picker": ["react-day-picker@9.12.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0", "date-fns-jalali": "^4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-t8OvG/Zrciso5CQJu5b1A7yzEmebvST+S3pOVQJWxwjjVngyG/CA2htN/D15dLI4uTEuLLkbZyS4YYt480FAtA=="],
+ "react-day-picker": ["react-day-picker@9.13.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0", "date-fns-jalali": "^4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-euzj5Hlq+lOHqI53NiuNhCP8HWgsPf/bBAVijR50hNaY1XwjKjShAnIe8jm8RD2W9IJUvihDIZ+KrmqfFzNhFQ=="],
"react-devtools-core": ["react-devtools-core@7.0.1", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw=="],
@@ -3060,9 +3027,7 @@
"react-fast-marquee": ["react-fast-marquee@1.6.5", "", { "peerDependencies": { "react": ">= 16.8.0 || ^18.0.0", "react-dom": ">= 16.8.0 || ^18.0.0" } }, "sha512-swDnPqrT2XISAih0o74zQVE2wQJFMvkx+9VZXYYNSLb/CUcAzU9pNj637Ar2+hyRw6b4tP6xh4GQZip2ZCpQpg=="],
- "react-hook-form": ["react-hook-form@7.68.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-oNN3fjrZ/Xo40SWlHf1yCjlMK417JxoSJVUXQjGdvdRCU07NTFei1i1f8ApUAts+IVh14e4EdakeLEA+BEAs/Q=="],
-
- "react-hotkeys-hook": ["react-hotkeys-hook@5.2.1", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xbKh6zJxd/vJHT4Bw4+0pBD662Fk20V+VFhLqciCg+manTVO4qlqRqiwFOYelfHN9dBvWj9vxaPkSS26ZSIJGg=="],
+ "react-hook-form": ["react-hook-form@7.69.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw=="],
"react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="],
@@ -3084,9 +3049,9 @@
"react-resizable-panels": ["react-resizable-panels@3.0.6", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew=="],
- "react-router": ["react-router@7.10.1", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw=="],
+ "react-router": ["react-router@7.11.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ=="],
- "react-router-dom": ["react-router-dom@7.10.1", "", { "dependencies": { "react-router": "7.10.1" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw=="],
+ "react-router-dom": ["react-router-dom@7.11.0", "", { "dependencies": { "react-router": "7.11.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g=="],
"react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="],
@@ -3230,12 +3195,10 @@
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
- "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
+ "rollup": ["rollup@4.54.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.54.0", "@rollup/rollup-android-arm64": "4.54.0", "@rollup/rollup-darwin-arm64": "4.54.0", "@rollup/rollup-darwin-x64": "4.54.0", "@rollup/rollup-freebsd-arm64": "4.54.0", "@rollup/rollup-freebsd-x64": "4.54.0", "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", "@rollup/rollup-linux-arm-musleabihf": "4.54.0", "@rollup/rollup-linux-arm64-gnu": "4.54.0", "@rollup/rollup-linux-arm64-musl": "4.54.0", "@rollup/rollup-linux-loong64-gnu": "4.54.0", "@rollup/rollup-linux-ppc64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-musl": "4.54.0", "@rollup/rollup-linux-s390x-gnu": "4.54.0", "@rollup/rollup-linux-x64-gnu": "4.54.0", "@rollup/rollup-linux-x64-musl": "4.54.0", "@rollup/rollup-openharmony-arm64": "4.54.0", "@rollup/rollup-win32-arm64-msvc": "4.54.0", "@rollup/rollup-win32-ia32-msvc": "4.54.0", "@rollup/rollup-win32-x64-gnu": "4.54.0", "@rollup/rollup-win32-x64-msvc": "4.54.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw=="],
"rollup-plugin-inject-process-env": ["rollup-plugin-inject-process-env@1.3.1", "", { "dependencies": { "magic-string": "^0.25.7" } }, "sha512-kKDoL30IZr0wxbNVJjq+OS92RJSKRbKV6B5eNW4q3mZTFqoWDh6lHy+mPDYuuGuERFNKXkG+AKxvYqC9+DRpKQ=="],
- "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
-
"roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
@@ -3262,13 +3225,13 @@
"semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
- "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
+ "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
"serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="],
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
- "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
+ "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
"server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="],
@@ -3304,7 +3267,7 @@
"sherif-windows-x64": ["sherif-windows-x64@1.9.0", "", { "os": "win32", "cpu": "x64" }, "sha512-3cL+XVGLpmyLC3UOZYiPr4vY2OFBQqPZnCoCwoRKN+ONm8VfGMirO9iqI0OckgFBUtJoG4AQY/MWxoPhNmzD8A=="],
- "shiki": ["shiki@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/engine-oniguruma": "3.19.0", "@shikijs/langs": "3.19.0", "@shikijs/themes": "3.19.0", "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA=="],
+ "shiki": ["shiki@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/engine-javascript": "3.20.0", "@shikijs/engine-oniguruma": "3.20.0", "@shikijs/langs": "3.20.0", "@shikijs/themes": "3.20.0", "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg=="],
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
@@ -3366,6 +3329,8 @@
"stacktrace-parser": ["stacktrace-parser@0.1.11", "", { "dependencies": { "type-fest": "^0.7.1" } }, "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg=="],
+ "standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="],
+
"stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="],
"state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="],
@@ -3376,6 +3341,8 @@
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
+ "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
+
"steno": ["steno@4.0.2", "", {}, "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A=="],
"streamdown": ["streamdown@1.6.10", "", { "dependencies": { "clsx": "^2.1.1", "hast": "^1.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "katex": "^0.16.22", "lucide-react": "^0.542.0", "marked": "^16.2.1", "mermaid": "^11.11.0", "rehype-harden": "^1.1.6", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", "remark-cjk-friendly": "^1.2.3", "remark-cjk-friendly-gfm-strikethrough": "^1.2.3", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remend": "1.0.1", "shiki": "^3.12.2", "tailwind-merge": "^3.3.1", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-B4Y3Z/qiXl1Dc+LzAB5c52Cd1QGRiFjaDwP+ERoj1JtCykdRDM8X6HwQnn3YkpkSk0x3R7S/6LrGe1nQiElHQQ=="],
@@ -3418,9 +3385,11 @@
"suspend-react": ["suspend-react@0.1.3", "", { "peerDependencies": { "react": ">=17.0" } }, "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ=="],
+ "swr": ["swr@2.3.4", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg=="],
+
"system-architecture": ["system-architecture@0.1.0", "", {}, "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA=="],
- "tabbable": ["tabbable@6.3.0", "", {}, "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ=="],
+ "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
@@ -3508,25 +3477,25 @@
"tunnel-rat": ["tunnel-rat@0.1.2", "", { "dependencies": { "zustand": "^4.3.2" } }, "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ=="],
- "turbo": ["turbo@2.6.3", "", { "optionalDependencies": { "turbo-darwin-64": "2.6.3", "turbo-darwin-arm64": "2.6.3", "turbo-linux-64": "2.6.3", "turbo-linux-arm64": "2.6.3", "turbo-windows-64": "2.6.3", "turbo-windows-arm64": "2.6.3" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bf6YKUv11l5Xfcmg76PyWoy/e2vbkkxFNBGJSnfdSXQC33ZiUfutYh6IXidc5MhsnrFkWfdNNLyaRk+kHMLlwA=="],
+ "turbo": ["turbo@2.7.2", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.2", "turbo-darwin-arm64": "2.7.2", "turbo-linux-64": "2.7.2", "turbo-linux-arm64": "2.7.2", "turbo-windows-64": "2.7.2", "turbo-windows-arm64": "2.7.2" }, "bin": { "turbo": "bin/turbo" } }, "sha512-5JIA5aYBAJSAhrhbyag1ZuMSgUZnHtI+Sq3H8D3an4fL8PeF+L1yYvbEJg47akP1PFfATMf5ehkqFnxfkmuwZQ=="],
- "turbo-darwin-64": ["turbo-darwin-64@2.6.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlJJDc1CQ7SK5Y5qnl7AzpkvKSnpkfPmnA+HeU/sgny3oHZckPV2776ebO2M33CYDSor7+8HQwaodY++IINhYg=="],
+ "turbo-darwin-64": ["turbo-darwin-64@2.7.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dxY3X6ezcT5vm3coK6VGixbrhplbQMwgNsCsvZamS/+/6JiebqW9DKt4NwpgYXhDY2HdH00I7FWs3wkVuan4rA=="],
- "turbo-darwin-arm64": ["turbo-darwin-arm64@2.6.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MwVt7rBKiOK7zdYerenfCRTypefw4kZCue35IJga9CH1+S50+KTiCkT6LBqo0hHeoH2iKuI0ldTF2a0aB72z3w=="],
+ "turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1bXmuwPLqNFt3mzrtYcVx1sdJ8UYb124Bf48nIgcpMCGZy3kDhgxNv1503kmuK/37OGOZbsWSQFU4I08feIuSg=="],
- "turbo-linux-64": ["turbo-linux-64@2.6.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cqpcw+dXxbnPtNnzeeSyWprjmuFVpHJqKcs7Jym5oXlu/ZcovEASUIUZVN3OGEM6Y/OTyyw0z09tOHNt5yBAVg=="],
+ "turbo-linux-64": ["turbo-linux-64@2.7.2", "", { "os": "linux", "cpu": "x64" }, "sha512-kP+TiiMaiPugbRlv57VGLfcjFNsFbo8H64wMBCPV2270Or2TpDCBULMzZrvEsvWFjT3pBFvToYbdp8/Kw0jAQg=="],
- "turbo-linux-arm64": ["turbo-linux-arm64@2.6.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-MterpZQmjXyr4uM7zOgFSFL3oRdNKeflY7nsjxJb2TklsYqiu3Z9pQ4zRVFFH8n0mLGna7MbQMZuKoWqqHb45w=="],
+ "turbo-linux-arm64": ["turbo-linux-arm64@2.7.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-VDJwQ0+8zjAfbyY6boNaWfP6RIez4ypKHxwkuB6SrWbOSk+vxTyW5/hEjytTwK8w/TsbKVcMDyvpora8tEsRFw=="],
- "turbo-windows-64": ["turbo-windows-64@2.6.3", "", { "os": "win32", "cpu": "x64" }, "sha512-biDU70v9dLwnBdLf+daoDlNJVvqOOP8YEjqNipBHzgclbQlXbsi6Gqqelp5er81Qo3BiRgmTNx79oaZQTPb07Q=="],
+ "turbo-windows-64": ["turbo-windows-64@2.7.2", "", { "os": "win32", "cpu": "x64" }, "sha512-rPjqQXVnI6A6oxgzNEE8DNb6Vdj2Wwyhfv3oDc+YM3U9P7CAcBIlKv/868mKl4vsBtz4ouWpTQNXG8vljgJO+w=="],
- "turbo-windows-arm64": ["turbo-windows-arm64@2.6.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-dDHVKpSeukah3VsI/xMEKeTnV9V9cjlpFSUs4bmsUiLu3Yv2ENlgVEZv65wxbeE0bh0jjpmElDT+P1KaCxArQQ=="],
+ "turbo-windows-arm64": ["turbo-windows-arm64@2.7.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-tcnHvBhO515OheIFWdxA+qUvZzNqqcHbLVFc1+n+TJ1rrp8prYicQtbtmsiKgMvr/54jb9jOabU62URAobnB7g=="],
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
- "twoslash": ["twoslash@0.3.4", "", { "dependencies": { "@typescript/vfs": "^1.6.1", "twoslash-protocol": "0.3.4" }, "peerDependencies": { "typescript": "^5.5.0" } }, "sha512-RtJURJlGRxrkJmTcZMjpr7jdYly1rfgpujJr1sBM9ch7SKVht/SjFk23IOAyvwT1NLCk+SJiMrvW4rIAUM2Wug=="],
+ "twoslash": ["twoslash@0.3.6", "", { "dependencies": { "@typescript/vfs": "^1.6.2", "twoslash-protocol": "0.3.6" }, "peerDependencies": { "typescript": "^5.5.0" } }, "sha512-VuI5OKl+MaUO9UIW3rXKoPgHI3X40ZgB/j12VY6h98Ae1mCBihjPvhOPeJWlxCYcmSbmeZt5ZKkK0dsVtp+6pA=="],
- "twoslash-protocol": ["twoslash-protocol@0.3.4", "", {}, "sha512-HHd7lzZNLUvjPzG/IE6js502gEzLC1x7HaO1up/f72d8G8ScWAs9Yfa97igelQRDl5h9tGcdFsRp+lNVre1EeQ=="],
+ "twoslash-protocol": ["twoslash-protocol@0.3.6", "", {}, "sha512-FHGsJ9Q+EsNr5bEbgG3hnbkvEBdW5STgPU824AHUjB4kw0Dn4p8tABT7Ncg1Ie6V0+mDg3Qpy41VafZXcQhWMA=="],
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
@@ -3582,7 +3551,7 @@
"unplugin": ["unplugin@1.0.1", "", { "dependencies": { "acorn": "^8.8.1", "chokidar": "^3.5.3", "webpack-sources": "^3.2.3", "webpack-virtual-modules": "^0.5.0" } }, "sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA=="],
- "update-browserslist-db": ["update-browserslist-db@1.2.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="],
+ "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
@@ -3618,7 +3587,7 @@
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
- "vite": ["vite@7.2.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ=="],
+ "vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="],
"vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="],
@@ -3634,7 +3603,7 @@
"vscode-uri": ["vscode-uri@3.0.8", "", {}, "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="],
- "watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="],
+ "watchpack": ["watchpack@2.5.0", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA=="],
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
@@ -3692,7 +3661,7 @@
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
- "zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="],
+ "zod": ["zod@4.3.4", "", {}, "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A=="],
"zustand": ["zustand@5.0.9", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg=="],
@@ -3704,6 +3673,12 @@
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+ "@chevrotain/cst-dts-gen/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
+
+ "@chevrotain/gast/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
+
+ "@clerk/shared/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
"@code-inspector/core/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"@code-inspector/core/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
@@ -3732,9 +3707,9 @@
"@electron/rebuild/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
- "@electron/universal/fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="],
+ "@electron/universal/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="],
- "@electron/windows-sign/fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="],
+ "@electron/windows-sign/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="],
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
@@ -3746,7 +3721,7 @@
"@mdx-js/mdx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
- "@neondatabase/serverless/@types/node": ["@types/node@22.19.2", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw=="],
+ "@neondatabase/serverless/@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="],
"@npmcli/move-file/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
@@ -3784,10 +3759,6 @@
"@react-three/drei/cross-env": ["cross-env@7.0.3", "", { "dependencies": { "cross-spawn": "^7.0.1" }, "bin": { "cross-env": "src/bin/cross-env.js", "cross-env-shell": "src/bin/cross-env-shell.js" } }, "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw=="],
- "@react-three/fiber/react-reconciler": ["react-reconciler@0.31.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="],
-
- "@react-three/fiber/scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="],
-
"@sentry-internal/feedback/@sentry/core": ["@sentry/core@10.29.0", "", {}, "sha512-olQ2DU9dA/Bwsz3PtA9KNXRMqBWRQSkPw+MxwWEoU1K1qtiM9L0j6lbEFb5iSY3d7WYD5MB+1d5COugjSBrHtw=="],
"@sentry-internal/replay/@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.29.0", "", { "dependencies": { "@sentry/core": "10.29.0" } }, "sha512-M3kycMY6f3KY9a8jDYac+yG0E3ZgWVWSxlOEC5MhYyX+g7mqxkwrb3LFQyuxSm/m+CCgMTCaPOOaB2twXP6EQg=="],
@@ -3822,7 +3793,7 @@
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
- "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="],
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
@@ -3838,7 +3809,7 @@
"@upstash/qstash/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
- "@vue/compiler-core/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+ "@vue/compiler-core/entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="],
"@xyflow/react/zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
@@ -3874,6 +3845,8 @@
"camelcase-keys/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
+ "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
+
"clipboardy/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
"cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -3906,9 +3879,11 @@
"dir-compare/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+ "dom-helpers/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
"dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
- "electron/@types/node": ["@types/node@22.19.2", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw=="],
+ "electron/@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="],
"electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -3922,6 +3897,8 @@
"execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+ "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
+
"extract-zip/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
"filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
@@ -3944,8 +3921,6 @@
"iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="],
- "its-fine/@types/react-reconciler": ["@types/react-reconciler@0.28.9", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="],
-
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
@@ -3978,6 +3953,8 @@
"meow/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
+ "mermaid/dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
+
"mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
"mermaid/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
@@ -4026,6 +4003,8 @@
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+ "randombytes/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
"raw-body/iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="],
"react-arborist/react-dnd": ["react-dnd@14.0.5", "", { "dependencies": { "@react-dnd/invariant": "^2.0.0", "@react-dnd/shallowequal": "^2.0.0", "dnd-core": "14.0.1", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { "@types/hoist-non-react-statics": ">= 3.3.1", "@types/node": ">= 12", "@types/react": ">= 16", "react": ">= 16.14" }, "optionalPeers": ["@types/hoist-non-react-statics", "@types/node", "@types/react"] }, "sha512-9i1jSgbyVw0ELlEVt/NkCUkxy1hmhJOkePoCH713u75vzHGyXhPDm28oLfc2NMSBjZRM1Y+wRjHXJT3sPrTy+A=="],
@@ -4038,8 +4017,6 @@
"react-mosaic-component/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
- "react-router/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
-
"read-pkg/normalize-package-data": ["normalize-package-data@3.0.3", "", { "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" } }, "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA=="],
"read-pkg/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
@@ -4106,10 +4083,14 @@
"tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
- "tsx/esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="],
+ "tsx/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
+
+ "tunnel-agent/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"tunnel-rat/zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
+ "vite/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
+
"webpack/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
@@ -4210,6 +4191,8 @@
"ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "bl/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+
"builder-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"cacache/glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
@@ -4310,59 +4293,113 @@
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+ "tar-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
+
"temp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="],
+ "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
+
+ "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="],
+
+ "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="],
+
+ "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="],
+
+ "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="],
+
+ "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="],
+
+ "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="],
+
+ "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="],
- "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="],
+ "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="],
- "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="],
+ "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="],
- "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="],
+ "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="],
- "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="],
+ "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="],
- "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="],
+ "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="],
- "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="],
+ "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="],
- "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="],
+ "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="],
- "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="],
+ "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="],
- "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="],
+ "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="],
- "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="],
+ "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="],
- "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="],
+ "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="],
- "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="],
+ "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="],
- "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="],
+ "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="],
- "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="],
+ "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="],
- "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="],
+ "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="],
- "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="],
+ "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="],
- "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="],
+ "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="],
- "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="],
+ "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="],
- "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="],
+ "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
- "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="],
+ "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="],
- "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="],
+ "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="],
- "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="],
+ "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="],
- "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="],
+ "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="],
- "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="],
+ "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="],
- "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="],
+ "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="],
+
+ "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="],
+
+ "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="],
+
+ "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="],
+
+ "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="],
+
+ "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="],
+
+ "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="],
+
+ "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="],
+
+ "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="],
+
+ "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="],
+
+ "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="],
+
+ "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="],
+
+ "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="],
+
+ "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="],
+
+ "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="],
+
+ "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="],
+
+ "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="],
+
+ "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="],
+
+ "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="],
+
+ "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="],
"webpack/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
@@ -4386,6 +4423,8 @@
"@uiw/react-markdown-preview/rehype-prism-plus/refractor/hastscript": ["hastscript@7.2.0", "", { "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^3.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw=="],
+ "bl/readable-stream/string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
"cacache/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"clipboardy/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
@@ -4422,6 +4461,8 @@
"rehype-prism-plus/refractor/hastscript/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
+ "tar-stream/readable-stream/string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
"temp/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
diff --git a/opencode.json b/opencode.json
new file mode 100644
index 000000000..f06f35457
--- /dev/null
+++ b/opencode.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://opencode.ai/config.json",
+ "permission": {
+ "external_directory": "allow"
+ }
+}
diff --git a/packages/auth/package.json b/packages/auth/package.json
deleted file mode 100644
index 75c35148f..000000000
--- a/packages/auth/package.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "name": "@superset/auth",
- "version": "0.1.0",
- "private": true,
- "type": "module",
- "exports": {
- ".": {
- "types": "./src/index.ts",
- "default": "./src/index.ts"
- },
- "./client": {
- "types": "./src/client.ts",
- "default": "./src/client.ts"
- }
- },
- "scripts": {
- "clean": "git clean -xdf .cache .turbo dist node_modules",
- "typecheck": "tsc --noEmit --emitDeclarationOnly false"
- },
- "dependencies": {
- "@superset/db": "workspace:*",
- "@t3-oss/env-core": "^0.13.8",
- "@t3-oss/env-nextjs": "^0.13.8",
- "better-auth": "^1.4.9",
- "dotenv": "^17.2.3",
- "drizzle-orm": "0.45.1",
- "zod": "^4.1.13"
- },
- "devDependencies": {
- "@superset/typescript": "workspace:*",
- "@types/node": "^24.9.1",
- "typescript": "^5.9.3"
- }
-}
diff --git a/packages/auth/src/client.ts b/packages/auth/src/client.ts
deleted file mode 100644
index ff1c1b8d7..000000000
--- a/packages/auth/src/client.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-"use client";
-
-import { organizationClient } from "better-auth/client/plugins";
-import { createAuthClient } from "better-auth/react";
-
-export const authClient = createAuthClient({
- baseURL: process.env.NEXT_PUBLIC_API_URL,
- plugins: [organizationClient()],
-});
diff --git a/packages/auth/src/env.ts b/packages/auth/src/env.ts
deleted file mode 100644
index 3f47ba0f5..000000000
--- a/packages/auth/src/env.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import path from "node:path";
-import { createEnv } from "@t3-oss/env-core";
-import { config } from "dotenv";
-import { z } from "zod";
-
-config({ path: path.resolve(process.cwd(), "../../../.env") });
-
-export const env = createEnv({
- server: {
- GH_CLIENT_ID: z.string(),
- GH_CLIENT_SECRET: z.string(),
- GOOGLE_CLIENT_ID: z.string(),
- GOOGLE_CLIENT_SECRET: z.string(),
- BETTER_AUTH_SECRET: z.string(),
- },
- clientPrefix: "NEXT_PUBLIC_",
- client: {
- NEXT_PUBLIC_COOKIE_DOMAIN: z.string(),
- NEXT_PUBLIC_API_URL: z.string().url(),
- NEXT_PUBLIC_WEB_URL: z.string().url(),
- NEXT_PUBLIC_ADMIN_URL: z.string().url(),
- NEXT_PUBLIC_MARKETING_URL: z.string().url(),
- },
- runtimeEnv: process.env,
- emptyStringAsUndefined: true,
- skipValidation: true,
-});
diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts
deleted file mode 100644
index 6a5482bb0..000000000
--- a/packages/auth/src/index.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { db } from "@superset/db/client";
-import { members } from "@superset/db/schema";
-import * as authSchema from "@superset/db/schema/auth";
-import { betterAuth } from "better-auth";
-import { drizzleAdapter } from "better-auth/adapters/drizzle";
-import { bearer, organization } from "better-auth/plugins";
-import { eq } from "drizzle-orm";
-
-import { env } from "./env";
-
-export const auth = betterAuth({
- baseURL: env.NEXT_PUBLIC_API_URL,
- secret: env.BETTER_AUTH_SECRET,
- database: drizzleAdapter(db, {
- provider: "pg",
- usePlural: true,
- schema: authSchema,
- }),
- trustedOrigins: [
- env.NEXT_PUBLIC_WEB_URL,
- env.NEXT_PUBLIC_API_URL,
- env.NEXT_PUBLIC_MARKETING_URL,
- env.NEXT_PUBLIC_ADMIN_URL,
- ],
- session: {
- expiresIn: 60 * 60 * 24 * 30, // 30 days
- updateAge: 60 * 60 * 24, // refresh daily on activity
- cookieCache: {
- enabled: true,
- maxAge: 60 * 5, // 5 minutes
- },
- },
- advanced: {
- crossSubDomainCookies: {
- enabled: true,
- domain: env.NEXT_PUBLIC_COOKIE_DOMAIN,
- },
- database: {
- generateId: false,
- },
- },
- socialProviders: {
- github: {
- clientId: env.GH_CLIENT_ID,
- clientSecret: env.GH_CLIENT_SECRET,
- },
- google: {
- clientId: env.GOOGLE_CLIENT_ID,
- clientSecret: env.GOOGLE_CLIENT_SECRET,
- },
- },
- databaseHooks: {
- user: {
- create: {
- after: async (user) => {
- // Create organization for new user
- const org = await auth.api.createOrganization({
- body: {
- name: `${user.name}'s Workspace`,
- slug: `${user.id.slice(0, 8)}-workspace`,
- userId: user.id,
- },
- });
-
- // Update all sessions for this user to set the active organization
- // This handles sessions created during signup before the org existed
- if (org?.id) {
- await db
- .update(authSchema.sessions)
- .set({ activeOrganizationId: org.id })
- .where(eq(authSchema.sessions.userId, user.id));
- }
- },
- },
- },
- session: {
- create: {
- before: async (session) => {
- // Set initial active organization when session is created
- // This handles existing users who already have organizations
- const membership = await db.query.members.findFirst({
- where: eq(members.userId, session.userId),
- });
-
- return {
- data: {
- ...session,
- activeOrganizationId: membership?.organizationId,
- },
- };
- },
- },
- },
- },
- plugins: [
- organization({
- creatorRole: "owner",
- }),
- bearer(),
- ],
-});
-
-export type Session = typeof auth.$Infer.Session;
-export type User = typeof auth.$Infer.Session.user;
diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json
deleted file mode 100644
index 840bf8c9a..000000000
--- a/packages/auth/tsconfig.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "extends": "@superset/typescript/internal-package.json",
- "include": ["src"],
- "exclude": ["node_modules", "dist"]
-}
diff --git a/packages/db/drizzle/0005_back_up_user_tables.sql b/packages/db/drizzle/0005_back_up_user_tables.sql
deleted file mode 100644
index 6b205836e..000000000
--- a/packages/db/drizzle/0005_back_up_user_tables.sql
+++ /dev/null
@@ -1,51 +0,0 @@
-ALTER TABLE "organization_members" RENAME TO "organization_members_backup";--> statement-breakpoint
-ALTER TABLE "organizations" RENAME TO "organizations_backup";--> statement-breakpoint
-ALTER TABLE "users" RENAME TO "users_backup";--> statement-breakpoint
-ALTER TABLE "organization_members_backup" DROP CONSTRAINT "organization_members_unique";--> statement-breakpoint
-ALTER TABLE "organizations_backup" DROP CONSTRAINT "organizations_clerk_org_id_unique";--> statement-breakpoint
-ALTER TABLE "organizations_backup" DROP CONSTRAINT "organizations_slug_unique";--> statement-breakpoint
-ALTER TABLE "users_backup" DROP CONSTRAINT "users_clerk_id_unique";--> statement-breakpoint
-ALTER TABLE "users_backup" DROP CONSTRAINT "users_email_unique";--> statement-breakpoint
-ALTER TABLE "integration_connections" DROP CONSTRAINT "integration_connections_organization_id_organizations_id_fk";
---> statement-breakpoint
-ALTER TABLE "integration_connections" DROP CONSTRAINT "integration_connections_connected_by_user_id_users_id_fk";
---> statement-breakpoint
-ALTER TABLE "organization_members_backup" DROP CONSTRAINT "organization_members_organization_id_organizations_id_fk";
---> statement-breakpoint
-ALTER TABLE "organization_members_backup" DROP CONSTRAINT "organization_members_user_id_users_id_fk";
---> statement-breakpoint
-ALTER TABLE "repositories" DROP CONSTRAINT "repositories_organization_id_organizations_id_fk";
---> statement-breakpoint
-ALTER TABLE "tasks" DROP CONSTRAINT "tasks_organization_id_organizations_id_fk";
---> statement-breakpoint
-ALTER TABLE "tasks" DROP CONSTRAINT "tasks_assignee_id_users_id_fk";
---> statement-breakpoint
-ALTER TABLE "tasks" DROP CONSTRAINT "tasks_creator_id_users_id_fk";
---> statement-breakpoint
-DROP INDEX "organization_members_organization_id_idx";--> statement-breakpoint
-DROP INDEX "organization_members_user_id_idx";--> statement-breakpoint
-DROP INDEX "organizations_slug_idx";--> statement-breakpoint
-DROP INDEX "organizations_clerk_org_id_idx";--> statement-breakpoint
-DROP INDEX "users_email_idx";--> statement-breakpoint
-DROP INDEX "users_clerk_id_idx";--> statement-breakpoint
-DROP INDEX "users_deleted_at_idx";--> statement-breakpoint
-ALTER TABLE "integration_connections" ADD CONSTRAINT "integration_connections_organization_id_organizations_backup_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "integration_connections" ADD CONSTRAINT "integration_connections_connected_by_user_id_users_backup_id_fk" FOREIGN KEY ("connected_by_user_id") REFERENCES "public"."users_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "organization_members_backup" ADD CONSTRAINT "organization_members_backup_organization_id_organizations_backup_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "organization_members_backup" ADD CONSTRAINT "organization_members_backup_user_id_users_backup_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "repositories" ADD CONSTRAINT "repositories_organization_id_organizations_backup_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "tasks" ADD CONSTRAINT "tasks_organization_id_organizations_backup_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "tasks" ADD CONSTRAINT "tasks_assignee_id_users_backup_id_fk" FOREIGN KEY ("assignee_id") REFERENCES "public"."users_backup"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "tasks" ADD CONSTRAINT "tasks_creator_id_users_backup_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users_backup"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-CREATE INDEX "organization_members_backup_organization_id_idx" ON "organization_members_backup" USING btree ("organization_id");--> statement-breakpoint
-CREATE INDEX "organization_members_backup_user_id_idx" ON "organization_members_backup" USING btree ("user_id");--> statement-breakpoint
-CREATE INDEX "organizations_backup_slug_idx" ON "organizations_backup" USING btree ("slug");--> statement-breakpoint
-CREATE INDEX "organizations_backup_clerk_org_id_idx" ON "organizations_backup" USING btree ("clerk_org_id");--> statement-breakpoint
-CREATE INDEX "users_backup_email_idx" ON "users_backup" USING btree ("email");--> statement-breakpoint
-CREATE INDEX "users_backup_clerk_id_idx" ON "users_backup" USING btree ("clerk_id");--> statement-breakpoint
-CREATE INDEX "users_backup_deleted_at_idx" ON "users_backup" USING btree ("deleted_at");--> statement-breakpoint
-ALTER TABLE "organization_members_backup" ADD CONSTRAINT "organization_members_backup_unique" UNIQUE("organization_id","user_id");--> statement-breakpoint
-ALTER TABLE "organizations_backup" ADD CONSTRAINT "organizations_backup_clerk_org_id_unique" UNIQUE("clerk_org_id");--> statement-breakpoint
-ALTER TABLE "organizations_backup" ADD CONSTRAINT "organizations_backup_slug_unique" UNIQUE("slug");--> statement-breakpoint
-ALTER TABLE "users_backup" ADD CONSTRAINT "users_backup_clerk_id_unique" UNIQUE("clerk_id");--> statement-breakpoint
-ALTER TABLE "users_backup" ADD CONSTRAINT "users_backup_email_unique" UNIQUE("email");
\ No newline at end of file
diff --git a/packages/db/drizzle/0006_add_better_auth_tables.sql b/packages/db/drizzle/0006_add_better_auth_tables.sql
deleted file mode 100644
index 4ea536feb..000000000
--- a/packages/db/drizzle/0006_add_better_auth_tables.sql
+++ /dev/null
@@ -1,179 +0,0 @@
-CREATE SCHEMA "auth";
---> statement-breakpoint
-CREATE TABLE "auth"."accounts" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "account_id" text NOT NULL,
- "provider_id" text NOT NULL,
- "user_id" uuid NOT NULL,
- "access_token" text,
- "refresh_token" text,
- "id_token" text,
- "access_token_expires_at" timestamp,
- "refresh_token_expires_at" timestamp,
- "scope" text,
- "password" text,
- "created_at" timestamp DEFAULT now() NOT NULL,
- "updated_at" timestamp NOT NULL
-);
---> statement-breakpoint
-CREATE TABLE "auth"."invitations" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "organization_id" uuid NOT NULL,
- "email" text NOT NULL,
- "role" text,
- "status" text DEFAULT 'pending' NOT NULL,
- "expires_at" timestamp NOT NULL,
- "created_at" timestamp DEFAULT now() NOT NULL,
- "inviter_id" uuid NOT NULL
-);
---> statement-breakpoint
-CREATE TABLE "auth"."members" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "organization_id" uuid NOT NULL,
- "user_id" uuid NOT NULL,
- "role" text DEFAULT 'member' NOT NULL,
- "created_at" timestamp NOT NULL
-);
---> statement-breakpoint
-CREATE TABLE "auth"."organizations" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "name" text NOT NULL,
- "slug" text NOT NULL,
- "logo" text,
- "created_at" timestamp NOT NULL,
- "metadata" text,
- CONSTRAINT "organizations_slug_unique" UNIQUE("slug")
-);
---> statement-breakpoint
-CREATE TABLE "auth"."sessions" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "expires_at" timestamp NOT NULL,
- "token" text NOT NULL,
- "created_at" timestamp DEFAULT now() NOT NULL,
- "updated_at" timestamp NOT NULL,
- "ip_address" text,
- "user_agent" text,
- "user_id" uuid NOT NULL,
- "active_organization_id" uuid,
- CONSTRAINT "sessions_token_unique" UNIQUE("token")
-);
---> statement-breakpoint
-CREATE TABLE "auth"."users" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "name" text NOT NULL,
- "email" text NOT NULL,
- "email_verified" boolean DEFAULT false NOT NULL,
- "image" text,
- "created_at" timestamp DEFAULT now() NOT NULL,
- "updated_at" timestamp DEFAULT now() NOT NULL,
- CONSTRAINT "users_email_unique" UNIQUE("email")
-);
---> statement-breakpoint
-CREATE TABLE "auth"."verifications" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "identifier" text NOT NULL,
- "value" text NOT NULL,
- "expires_at" timestamp NOT NULL,
- "created_at" timestamp DEFAULT now() NOT NULL,
- "updated_at" timestamp DEFAULT now() NOT NULL
-);
---> statement-breakpoint
--- Migrate users from backup (keeping same UUIDs)
-INSERT INTO "auth"."users" (id, name, email, email_verified, image, created_at, updated_at)
-SELECT
- id,
- name,
- email,
- true,
- NULL,
- created_at,
- updated_at
-FROM users_backup
-WHERE deleted_at IS NULL
-ON CONFLICT (id) DO NOTHING;
---> statement-breakpoint
--- Migrate organizations from backup (keeping same UUIDs)
-INSERT INTO "auth"."organizations" (id, name, slug, logo, created_at, metadata)
-SELECT
- id,
- name,
- slug,
- avatar_url,
- created_at,
- NULL
-FROM organizations_backup
-ON CONFLICT (id) DO NOTHING;
---> statement-breakpoint
--- Migrate members from backup (keeping same UUIDs)
-INSERT INTO "auth"."members" (id, organization_id, user_id, role, created_at)
-SELECT
- id,
- organization_id,
- user_id,
- role,
- created_at
-FROM organization_members_backup
-WHERE user_id IN (SELECT id FROM users_backup WHERE deleted_at IS NULL)
-ON CONFLICT (id) DO NOTHING;
---> statement-breakpoint
--- Backfill: Create personal workspace for users without an org
-INSERT INTO "auth"."organizations" (id, name, slug, created_at)
-SELECT
- gen_random_uuid(),
- u.name || '''s Workspace',
- LEFT(u.id::text, 8) || '-workspace',
- NOW()
-FROM "auth"."users" u
-WHERE NOT EXISTS (
- SELECT 1 FROM "auth"."members" m WHERE m.user_id = u.id
-);
---> statement-breakpoint
--- Backfill: Add users as owners of their new workspaces
-INSERT INTO "auth"."members" (id, organization_id, user_id, role, created_at)
-SELECT
- gen_random_uuid(),
- o.id,
- u.id,
- 'owner',
- NOW()
-FROM "auth"."users" u
-JOIN "auth"."organizations" o ON o.slug = LEFT(u.id::text, 8) || '-workspace'
-WHERE NOT EXISTS (
- SELECT 1 FROM "auth"."members" m WHERE m.user_id = u.id
-);
---> statement-breakpoint
-DROP TABLE IF EXISTS "organization_members_backup" CASCADE;--> statement-breakpoint
-DROP TABLE IF EXISTS "organizations_backup" CASCADE;--> statement-breakpoint
-DROP TABLE IF EXISTS "users_backup" CASCADE;--> statement-breakpoint
-ALTER TABLE "integration_connections" DROP CONSTRAINT IF EXISTS "integration_connections_organization_id_organizations_backup_id_fk";
---> statement-breakpoint
-ALTER TABLE "integration_connections" DROP CONSTRAINT IF EXISTS "integration_connections_connected_by_user_id_users_backup_id_fk";
---> statement-breakpoint
-ALTER TABLE "repositories" DROP CONSTRAINT IF EXISTS "repositories_organization_id_organizations_backup_id_fk";
---> statement-breakpoint
-ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_organization_id_organizations_backup_id_fk";
---> statement-breakpoint
-ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_assignee_id_users_backup_id_fk";
---> statement-breakpoint
-ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_creator_id_users_backup_id_fk";
---> statement-breakpoint
-ALTER TABLE "auth"."accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "auth"."invitations" ADD CONSTRAINT "invitations_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "auth"."invitations" ADD CONSTRAINT "invitations_inviter_id_users_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "auth"."members" ADD CONSTRAINT "members_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "auth"."members" ADD CONSTRAINT "members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "auth"."sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-CREATE INDEX "accounts_user_id_idx" ON "auth"."accounts" USING btree ("user_id");--> statement-breakpoint
-CREATE INDEX "invitations_organization_id_idx" ON "auth"."invitations" USING btree ("organization_id");--> statement-breakpoint
-CREATE INDEX "invitations_email_idx" ON "auth"."invitations" USING btree ("email");--> statement-breakpoint
-CREATE INDEX "members_organization_id_idx" ON "auth"."members" USING btree ("organization_id");--> statement-breakpoint
-CREATE INDEX "members_user_id_idx" ON "auth"."members" USING btree ("user_id");--> statement-breakpoint
-CREATE UNIQUE INDEX "organizations_slug_idx" ON "auth"."organizations" USING btree ("slug");--> statement-breakpoint
-CREATE INDEX "sessions_user_id_idx" ON "auth"."sessions" USING btree ("user_id");--> statement-breakpoint
-CREATE INDEX "verifications_identifier_idx" ON "auth"."verifications" USING btree ("identifier");--> statement-breakpoint
-ALTER TABLE "integration_connections" ADD CONSTRAINT "integration_connections_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "integration_connections" ADD CONSTRAINT "integration_connections_connected_by_user_id_users_id_fk" FOREIGN KEY ("connected_by_user_id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "repositories" ADD CONSTRAINT "repositories_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "tasks" ADD CONSTRAINT "tasks_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "tasks" ADD CONSTRAINT "tasks_assignee_id_users_id_fk" FOREIGN KEY ("assignee_id") REFERENCES "auth"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "tasks" ADD CONSTRAINT "tasks_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/packages/db/drizzle/0007_add_created_at_default.sql b/packages/db/drizzle/0007_add_created_at_default.sql
deleted file mode 100644
index ec35981eb..000000000
--- a/packages/db/drizzle/0007_add_created_at_default.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-ALTER TABLE "auth"."members" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
-ALTER TABLE "auth"."organizations" ALTER COLUMN "created_at" SET DEFAULT now();
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/0005_snapshot.json b/packages/db/drizzle/meta/0005_snapshot.json
deleted file mode 100644
index edb566a02..000000000
--- a/packages/db/drizzle/meta/0005_snapshot.json
+++ /dev/null
@@ -1,1194 +0,0 @@
-{
- "id": "2bbde624-dc07-4526-ad9b-c4bb89d91b11",
- "prevId": "a3a097c7-d242-4bec-8d35-7085b03b73c2",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "ingest.webhook_events": {
- "name": "webhook_events",
- "schema": "ingest",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "provider": {
- "name": "provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true
- },
- "event_id": {
- "name": "event_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "event_type": {
- "name": "event_type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "payload": {
- "name": "payload",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'pending'"
- },
- "processed_at": {
- "name": "processed_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "error": {
- "name": "error",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "retry_count": {
- "name": "retry_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "received_at": {
- "name": "received_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "webhook_events_provider_status_idx": {
- "name": "webhook_events_provider_status_idx",
- "columns": [
- {
- "expression": "provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "status",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "webhook_events_provider_event_id_idx": {
- "name": "webhook_events_provider_event_id_idx",
- "columns": [
- {
- "expression": "provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "event_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": true,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "webhook_events_received_at_idx": {
- "name": "webhook_events_received_at_idx",
- "columns": [
- {
- "expression": "received_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.integration_connections": {
- "name": "integration_connections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "connected_by_user_id": {
- "name": "connected_by_user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "provider": {
- "name": "provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true
- },
- "access_token": {
- "name": "access_token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "refresh_token": {
- "name": "refresh_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "token_expires_at": {
- "name": "token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "external_org_id": {
- "name": "external_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_org_name": {
- "name": "external_org_name",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "config": {
- "name": "config",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "integration_connections_org_idx": {
- "name": "integration_connections_org_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "integration_connections_organization_id_organizations_backup_id_fk": {
- "name": "integration_connections_organization_id_organizations_backup_id_fk",
- "tableFrom": "integration_connections",
- "tableTo": "organizations_backup",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "integration_connections_connected_by_user_id_users_backup_id_fk": {
- "name": "integration_connections_connected_by_user_id_users_backup_id_fk",
- "tableFrom": "integration_connections",
- "tableTo": "users_backup",
- "columnsFrom": [
- "connected_by_user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "integration_connections_unique": {
- "name": "integration_connections_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "provider"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.organization_members_backup": {
- "name": "organization_members_backup",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'member'"
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "organization_members_backup_organization_id_idx": {
- "name": "organization_members_backup_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "organization_members_backup_user_id_idx": {
- "name": "organization_members_backup_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "organization_members_backup_organization_id_organizations_backup_id_fk": {
- "name": "organization_members_backup_organization_id_organizations_backup_id_fk",
- "tableFrom": "organization_members_backup",
- "tableTo": "organizations_backup",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "organization_members_backup_user_id_users_backup_id_fk": {
- "name": "organization_members_backup_user_id_users_backup_id_fk",
- "tableFrom": "organization_members_backup",
- "tableTo": "users_backup",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "organization_members_backup_unique": {
- "name": "organization_members_backup_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "user_id"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.organizations_backup": {
- "name": "organizations_backup",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "clerk_org_id": {
- "name": "clerk_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "github_org": {
- "name": "github_org",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "organizations_backup_slug_idx": {
- "name": "organizations_backup_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "organizations_backup_clerk_org_id_idx": {
- "name": "organizations_backup_clerk_org_id_idx",
- "columns": [
- {
- "expression": "clerk_org_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "organizations_backup_clerk_org_id_unique": {
- "name": "organizations_backup_clerk_org_id_unique",
- "nullsNotDistinct": false,
- "columns": [
- "clerk_org_id"
- ]
- },
- "organizations_backup_slug_unique": {
- "name": "organizations_backup_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "slug"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.repositories": {
- "name": "repositories",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_url": {
- "name": "repo_url",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_owner": {
- "name": "repo_owner",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_name": {
- "name": "repo_name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "default_branch": {
- "name": "default_branch",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'main'"
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "repositories_organization_id_idx": {
- "name": "repositories_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "repositories_slug_idx": {
- "name": "repositories_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "repositories_organization_id_organizations_backup_id_fk": {
- "name": "repositories_organization_id_organizations_backup_id_fk",
- "tableFrom": "repositories",
- "tableTo": "organizations_backup",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "repositories_org_slug_unique": {
- "name": "repositories_org_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "slug"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.tasks": {
- "name": "tasks",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "title": {
- "name": "title",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status_color": {
- "name": "status_color",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status_type": {
- "name": "status_type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status_position": {
- "name": "status_position",
- "type": "real",
- "primaryKey": false,
- "notNull": false
- },
- "priority": {
- "name": "priority",
- "type": "task_priority",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true,
- "default": "'none'"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "repository_id": {
- "name": "repository_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "assignee_id": {
- "name": "assignee_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "creator_id": {
- "name": "creator_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "estimate": {
- "name": "estimate",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "due_date": {
- "name": "due_date",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "labels": {
- "name": "labels",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false,
- "default": "'[]'::jsonb"
- },
- "branch": {
- "name": "branch",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pr_url": {
- "name": "pr_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_provider": {
- "name": "external_provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": false
- },
- "external_id": {
- "name": "external_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_key": {
- "name": "external_key",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_url": {
- "name": "external_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "last_synced_at": {
- "name": "last_synced_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "sync_error": {
- "name": "sync_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "completed_at": {
- "name": "completed_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "deleted_at": {
- "name": "deleted_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "tasks_slug_idx": {
- "name": "tasks_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_organization_id_idx": {
- "name": "tasks_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_repository_id_idx": {
- "name": "tasks_repository_id_idx",
- "columns": [
- {
- "expression": "repository_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_assignee_id_idx": {
- "name": "tasks_assignee_id_idx",
- "columns": [
- {
- "expression": "assignee_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_creator_id_idx": {
- "name": "tasks_creator_id_idx",
- "columns": [
- {
- "expression": "creator_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_status_idx": {
- "name": "tasks_status_idx",
- "columns": [
- {
- "expression": "status",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_created_at_idx": {
- "name": "tasks_created_at_idx",
- "columns": [
- {
- "expression": "created_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_external_provider_idx": {
- "name": "tasks_external_provider_idx",
- "columns": [
- {
- "expression": "external_provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "tasks_organization_id_organizations_backup_id_fk": {
- "name": "tasks_organization_id_organizations_backup_id_fk",
- "tableFrom": "tasks",
- "tableTo": "organizations_backup",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "tasks_repository_id_repositories_id_fk": {
- "name": "tasks_repository_id_repositories_id_fk",
- "tableFrom": "tasks",
- "tableTo": "repositories",
- "columnsFrom": [
- "repository_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "tasks_assignee_id_users_backup_id_fk": {
- "name": "tasks_assignee_id_users_backup_id_fk",
- "tableFrom": "tasks",
- "tableTo": "users_backup",
- "columnsFrom": [
- "assignee_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "set null",
- "onUpdate": "no action"
- },
- "tasks_creator_id_users_backup_id_fk": {
- "name": "tasks_creator_id_users_backup_id_fk",
- "tableFrom": "tasks",
- "tableTo": "users_backup",
- "columnsFrom": [
- "creator_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "tasks_slug_unique": {
- "name": "tasks_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "slug"
- ]
- },
- "tasks_external_unique": {
- "name": "tasks_external_unique",
- "nullsNotDistinct": false,
- "columns": [
- "external_provider",
- "external_id"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.users_backup": {
- "name": "users_backup",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "clerk_id": {
- "name": "clerk_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "deleted_at": {
- "name": "deleted_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "users_backup_email_idx": {
- "name": "users_backup_email_idx",
- "columns": [
- {
- "expression": "email",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "users_backup_clerk_id_idx": {
- "name": "users_backup_clerk_id_idx",
- "columns": [
- {
- "expression": "clerk_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "users_backup_deleted_at_idx": {
- "name": "users_backup_deleted_at_idx",
- "columns": [
- {
- "expression": "deleted_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "users_backup_clerk_id_unique": {
- "name": "users_backup_clerk_id_unique",
- "nullsNotDistinct": false,
- "columns": [
- "clerk_id"
- ]
- },
- "users_backup_email_unique": {
- "name": "users_backup_email_unique",
- "nullsNotDistinct": false,
- "columns": [
- "email"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {
- "public.integration_provider": {
- "name": "integration_provider",
- "schema": "public",
- "values": [
- "linear"
- ]
- },
- "public.task_priority": {
- "name": "task_priority",
- "schema": "public",
- "values": [
- "urgent",
- "high",
- "medium",
- "low",
- "none"
- ]
- },
- "public.task_status": {
- "name": "task_status",
- "schema": "public",
- "values": [
- "backlog",
- "todo",
- "planning",
- "working",
- "needs-feedback",
- "ready-to-merge",
- "completed",
- "canceled"
- ]
- }
- },
- "schemas": {
- "ingest": "ingest"
- },
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/0006_snapshot.json b/packages/db/drizzle/meta/0006_snapshot.json
deleted file mode 100644
index 113cceb67..000000000
--- a/packages/db/drizzle/meta/0006_snapshot.json
+++ /dev/null
@@ -1,1523 +0,0 @@
-{
- "id": "711d4546-1abc-4aad-ac38-edd0f1be7be4",
- "prevId": "2bbde624-dc07-4526-ad9b-c4bb89d91b11",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "auth.accounts": {
- "name": "accounts",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "provider_id": {
- "name": "provider_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "access_token": {
- "name": "access_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "refresh_token": {
- "name": "refresh_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "id_token": {
- "name": "id_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "access_token_expires_at": {
- "name": "access_token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "refresh_token_expires_at": {
- "name": "refresh_token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "password": {
- "name": "password",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "accounts_user_id_idx": {
- "name": "accounts_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "accounts_user_id_users_id_fk": {
- "name": "accounts_user_id_users_id_fk",
- "tableFrom": "accounts",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.invitations": {
- "name": "invitations",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'pending'"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "inviter_id": {
- "name": "inviter_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "invitations_organization_id_idx": {
- "name": "invitations_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "invitations_email_idx": {
- "name": "invitations_email_idx",
- "columns": [
- {
- "expression": "email",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "invitations_organization_id_organizations_id_fk": {
- "name": "invitations_organization_id_organizations_id_fk",
- "tableFrom": "invitations",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "invitations_inviter_id_users_id_fk": {
- "name": "invitations_inviter_id_users_id_fk",
- "tableFrom": "invitations",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "inviter_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.members": {
- "name": "members",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'member'"
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "members_organization_id_idx": {
- "name": "members_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "members_user_id_idx": {
- "name": "members_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "members_organization_id_organizations_id_fk": {
- "name": "members_organization_id_organizations_id_fk",
- "tableFrom": "members",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "members_user_id_users_id_fk": {
- "name": "members_user_id_users_id_fk",
- "tableFrom": "members",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.organizations": {
- "name": "organizations",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "logo": {
- "name": "logo",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "metadata": {
- "name": "metadata",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {
- "organizations_slug_idx": {
- "name": "organizations_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": true,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "organizations_slug_unique": {
- "name": "organizations_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "slug"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.sessions": {
- "name": "sessions",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "active_organization_id": {
- "name": "active_organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {
- "sessions_user_id_idx": {
- "name": "sessions_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "sessions_user_id_users_id_fk": {
- "name": "sessions_user_id_users_id_fk",
- "tableFrom": "sessions",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "sessions_token_unique": {
- "name": "sessions_token_unique",
- "nullsNotDistinct": false,
- "columns": [
- "token"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.users": {
- "name": "users",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "image": {
- "name": "image",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "users_email_unique": {
- "name": "users_email_unique",
- "nullsNotDistinct": false,
- "columns": [
- "email"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.verifications": {
- "name": "verifications",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "identifier": {
- "name": "identifier",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "value": {
- "name": "value",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "verifications_identifier_idx": {
- "name": "verifications_identifier_idx",
- "columns": [
- {
- "expression": "identifier",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "ingest.webhook_events": {
- "name": "webhook_events",
- "schema": "ingest",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "provider": {
- "name": "provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true
- },
- "event_id": {
- "name": "event_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "event_type": {
- "name": "event_type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "payload": {
- "name": "payload",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'pending'"
- },
- "processed_at": {
- "name": "processed_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "error": {
- "name": "error",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "retry_count": {
- "name": "retry_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "received_at": {
- "name": "received_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "webhook_events_provider_status_idx": {
- "name": "webhook_events_provider_status_idx",
- "columns": [
- {
- "expression": "provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "status",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "webhook_events_provider_event_id_idx": {
- "name": "webhook_events_provider_event_id_idx",
- "columns": [
- {
- "expression": "provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "event_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": true,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "webhook_events_received_at_idx": {
- "name": "webhook_events_received_at_idx",
- "columns": [
- {
- "expression": "received_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.integration_connections": {
- "name": "integration_connections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "connected_by_user_id": {
- "name": "connected_by_user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "provider": {
- "name": "provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true
- },
- "access_token": {
- "name": "access_token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "refresh_token": {
- "name": "refresh_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "token_expires_at": {
- "name": "token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "external_org_id": {
- "name": "external_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_org_name": {
- "name": "external_org_name",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "config": {
- "name": "config",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "integration_connections_org_idx": {
- "name": "integration_connections_org_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "integration_connections_organization_id_organizations_id_fk": {
- "name": "integration_connections_organization_id_organizations_id_fk",
- "tableFrom": "integration_connections",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "integration_connections_connected_by_user_id_users_id_fk": {
- "name": "integration_connections_connected_by_user_id_users_id_fk",
- "tableFrom": "integration_connections",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "connected_by_user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "integration_connections_unique": {
- "name": "integration_connections_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "provider"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.repositories": {
- "name": "repositories",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_url": {
- "name": "repo_url",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_owner": {
- "name": "repo_owner",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_name": {
- "name": "repo_name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "default_branch": {
- "name": "default_branch",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'main'"
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "repositories_organization_id_idx": {
- "name": "repositories_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "repositories_slug_idx": {
- "name": "repositories_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "repositories_organization_id_organizations_id_fk": {
- "name": "repositories_organization_id_organizations_id_fk",
- "tableFrom": "repositories",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "repositories_org_slug_unique": {
- "name": "repositories_org_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "slug"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.tasks": {
- "name": "tasks",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "title": {
- "name": "title",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status_color": {
- "name": "status_color",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status_type": {
- "name": "status_type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status_position": {
- "name": "status_position",
- "type": "real",
- "primaryKey": false,
- "notNull": false
- },
- "priority": {
- "name": "priority",
- "type": "task_priority",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true,
- "default": "'none'"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "repository_id": {
- "name": "repository_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "assignee_id": {
- "name": "assignee_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "creator_id": {
- "name": "creator_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "estimate": {
- "name": "estimate",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "due_date": {
- "name": "due_date",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "labels": {
- "name": "labels",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false,
- "default": "'[]'::jsonb"
- },
- "branch": {
- "name": "branch",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pr_url": {
- "name": "pr_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_provider": {
- "name": "external_provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": false
- },
- "external_id": {
- "name": "external_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_key": {
- "name": "external_key",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_url": {
- "name": "external_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "last_synced_at": {
- "name": "last_synced_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "sync_error": {
- "name": "sync_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "completed_at": {
- "name": "completed_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "deleted_at": {
- "name": "deleted_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "tasks_slug_idx": {
- "name": "tasks_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_organization_id_idx": {
- "name": "tasks_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_repository_id_idx": {
- "name": "tasks_repository_id_idx",
- "columns": [
- {
- "expression": "repository_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_assignee_id_idx": {
- "name": "tasks_assignee_id_idx",
- "columns": [
- {
- "expression": "assignee_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_creator_id_idx": {
- "name": "tasks_creator_id_idx",
- "columns": [
- {
- "expression": "creator_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_status_idx": {
- "name": "tasks_status_idx",
- "columns": [
- {
- "expression": "status",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_created_at_idx": {
- "name": "tasks_created_at_idx",
- "columns": [
- {
- "expression": "created_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_external_provider_idx": {
- "name": "tasks_external_provider_idx",
- "columns": [
- {
- "expression": "external_provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "tasks_organization_id_organizations_id_fk": {
- "name": "tasks_organization_id_organizations_id_fk",
- "tableFrom": "tasks",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "tasks_repository_id_repositories_id_fk": {
- "name": "tasks_repository_id_repositories_id_fk",
- "tableFrom": "tasks",
- "tableTo": "repositories",
- "columnsFrom": [
- "repository_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "tasks_assignee_id_users_id_fk": {
- "name": "tasks_assignee_id_users_id_fk",
- "tableFrom": "tasks",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "assignee_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "set null",
- "onUpdate": "no action"
- },
- "tasks_creator_id_users_id_fk": {
- "name": "tasks_creator_id_users_id_fk",
- "tableFrom": "tasks",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "creator_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "tasks_slug_unique": {
- "name": "tasks_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "slug"
- ]
- },
- "tasks_external_unique": {
- "name": "tasks_external_unique",
- "nullsNotDistinct": false,
- "columns": [
- "external_provider",
- "external_id"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {
- "public.integration_provider": {
- "name": "integration_provider",
- "schema": "public",
- "values": [
- "linear"
- ]
- },
- "public.task_priority": {
- "name": "task_priority",
- "schema": "public",
- "values": [
- "urgent",
- "high",
- "medium",
- "low",
- "none"
- ]
- },
- "public.task_status": {
- "name": "task_status",
- "schema": "public",
- "values": [
- "backlog",
- "todo",
- "planning",
- "working",
- "needs-feedback",
- "ready-to-merge",
- "completed",
- "canceled"
- ]
- }
- },
- "schemas": {
- "auth": "auth",
- "ingest": "ingest"
- },
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/0007_snapshot.json b/packages/db/drizzle/meta/0007_snapshot.json
deleted file mode 100644
index 7ebd32ed7..000000000
--- a/packages/db/drizzle/meta/0007_snapshot.json
+++ /dev/null
@@ -1,1525 +0,0 @@
-{
- "id": "fa87a301-b667-4c9c-9e1f-15b9d9130bb0",
- "prevId": "711d4546-1abc-4aad-ac38-edd0f1be7be4",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "auth.accounts": {
- "name": "accounts",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "provider_id": {
- "name": "provider_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "access_token": {
- "name": "access_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "refresh_token": {
- "name": "refresh_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "id_token": {
- "name": "id_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "access_token_expires_at": {
- "name": "access_token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "refresh_token_expires_at": {
- "name": "refresh_token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "password": {
- "name": "password",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "accounts_user_id_idx": {
- "name": "accounts_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "accounts_user_id_users_id_fk": {
- "name": "accounts_user_id_users_id_fk",
- "tableFrom": "accounts",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.invitations": {
- "name": "invitations",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'pending'"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "inviter_id": {
- "name": "inviter_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "invitations_organization_id_idx": {
- "name": "invitations_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "invitations_email_idx": {
- "name": "invitations_email_idx",
- "columns": [
- {
- "expression": "email",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "invitations_organization_id_organizations_id_fk": {
- "name": "invitations_organization_id_organizations_id_fk",
- "tableFrom": "invitations",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "invitations_inviter_id_users_id_fk": {
- "name": "invitations_inviter_id_users_id_fk",
- "tableFrom": "invitations",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "inviter_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.members": {
- "name": "members",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'member'"
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "members_organization_id_idx": {
- "name": "members_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "members_user_id_idx": {
- "name": "members_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "members_organization_id_organizations_id_fk": {
- "name": "members_organization_id_organizations_id_fk",
- "tableFrom": "members",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "members_user_id_users_id_fk": {
- "name": "members_user_id_users_id_fk",
- "tableFrom": "members",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.organizations": {
- "name": "organizations",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "logo": {
- "name": "logo",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "metadata": {
- "name": "metadata",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {
- "organizations_slug_idx": {
- "name": "organizations_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": true,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "organizations_slug_unique": {
- "name": "organizations_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "slug"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.sessions": {
- "name": "sessions",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "active_organization_id": {
- "name": "active_organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {
- "sessions_user_id_idx": {
- "name": "sessions_user_id_idx",
- "columns": [
- {
- "expression": "user_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "sessions_user_id_users_id_fk": {
- "name": "sessions_user_id_users_id_fk",
- "tableFrom": "sessions",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "sessions_token_unique": {
- "name": "sessions_token_unique",
- "nullsNotDistinct": false,
- "columns": [
- "token"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.users": {
- "name": "users",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "image": {
- "name": "image",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "users_email_unique": {
- "name": "users_email_unique",
- "nullsNotDistinct": false,
- "columns": [
- "email"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "auth.verifications": {
- "name": "verifications",
- "schema": "auth",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "identifier": {
- "name": "identifier",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "value": {
- "name": "value",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "verifications_identifier_idx": {
- "name": "verifications_identifier_idx",
- "columns": [
- {
- "expression": "identifier",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "ingest.webhook_events": {
- "name": "webhook_events",
- "schema": "ingest",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "provider": {
- "name": "provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true
- },
- "event_id": {
- "name": "event_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "event_type": {
- "name": "event_type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "payload": {
- "name": "payload",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'pending'"
- },
- "processed_at": {
- "name": "processed_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "error": {
- "name": "error",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "retry_count": {
- "name": "retry_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "received_at": {
- "name": "received_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "webhook_events_provider_status_idx": {
- "name": "webhook_events_provider_status_idx",
- "columns": [
- {
- "expression": "provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "status",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "webhook_events_provider_event_id_idx": {
- "name": "webhook_events_provider_event_id_idx",
- "columns": [
- {
- "expression": "provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "event_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": true,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "webhook_events_received_at_idx": {
- "name": "webhook_events_received_at_idx",
- "columns": [
- {
- "expression": "received_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.integration_connections": {
- "name": "integration_connections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "connected_by_user_id": {
- "name": "connected_by_user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "provider": {
- "name": "provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true
- },
- "access_token": {
- "name": "access_token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "refresh_token": {
- "name": "refresh_token",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "token_expires_at": {
- "name": "token_expires_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "external_org_id": {
- "name": "external_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_org_name": {
- "name": "external_org_name",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "config": {
- "name": "config",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "integration_connections_org_idx": {
- "name": "integration_connections_org_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "integration_connections_organization_id_organizations_id_fk": {
- "name": "integration_connections_organization_id_organizations_id_fk",
- "tableFrom": "integration_connections",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "integration_connections_connected_by_user_id_users_id_fk": {
- "name": "integration_connections_connected_by_user_id_users_id_fk",
- "tableFrom": "integration_connections",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "connected_by_user_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "integration_connections_unique": {
- "name": "integration_connections_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "provider"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.repositories": {
- "name": "repositories",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_url": {
- "name": "repo_url",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_owner": {
- "name": "repo_owner",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "repo_name": {
- "name": "repo_name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "default_branch": {
- "name": "default_branch",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'main'"
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "repositories_organization_id_idx": {
- "name": "repositories_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "repositories_slug_idx": {
- "name": "repositories_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "repositories_organization_id_organizations_id_fk": {
- "name": "repositories_organization_id_organizations_id_fk",
- "tableFrom": "repositories",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "repositories_org_slug_unique": {
- "name": "repositories_org_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "organization_id",
- "slug"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.tasks": {
- "name": "tasks",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "title": {
- "name": "title",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status_color": {
- "name": "status_color",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status_type": {
- "name": "status_type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "status_position": {
- "name": "status_position",
- "type": "real",
- "primaryKey": false,
- "notNull": false
- },
- "priority": {
- "name": "priority",
- "type": "task_priority",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": true,
- "default": "'none'"
- },
- "organization_id": {
- "name": "organization_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "repository_id": {
- "name": "repository_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "assignee_id": {
- "name": "assignee_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "creator_id": {
- "name": "creator_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "estimate": {
- "name": "estimate",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "due_date": {
- "name": "due_date",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "labels": {
- "name": "labels",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false,
- "default": "'[]'::jsonb"
- },
- "branch": {
- "name": "branch",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pr_url": {
- "name": "pr_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_provider": {
- "name": "external_provider",
- "type": "integration_provider",
- "typeSchema": "public",
- "primaryKey": false,
- "notNull": false
- },
- "external_id": {
- "name": "external_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_key": {
- "name": "external_key",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "external_url": {
- "name": "external_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "last_synced_at": {
- "name": "last_synced_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "sync_error": {
- "name": "sync_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "completed_at": {
- "name": "completed_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "deleted_at": {
- "name": "deleted_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "tasks_slug_idx": {
- "name": "tasks_slug_idx",
- "columns": [
- {
- "expression": "slug",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_organization_id_idx": {
- "name": "tasks_organization_id_idx",
- "columns": [
- {
- "expression": "organization_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_repository_id_idx": {
- "name": "tasks_repository_id_idx",
- "columns": [
- {
- "expression": "repository_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_assignee_id_idx": {
- "name": "tasks_assignee_id_idx",
- "columns": [
- {
- "expression": "assignee_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_creator_id_idx": {
- "name": "tasks_creator_id_idx",
- "columns": [
- {
- "expression": "creator_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_status_idx": {
- "name": "tasks_status_idx",
- "columns": [
- {
- "expression": "status",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_created_at_idx": {
- "name": "tasks_created_at_idx",
- "columns": [
- {
- "expression": "created_at",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- },
- "tasks_external_provider_idx": {
- "name": "tasks_external_provider_idx",
- "columns": [
- {
- "expression": "external_provider",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "tasks_organization_id_organizations_id_fk": {
- "name": "tasks_organization_id_organizations_id_fk",
- "tableFrom": "tasks",
- "tableTo": "organizations",
- "schemaTo": "auth",
- "columnsFrom": [
- "organization_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "tasks_repository_id_repositories_id_fk": {
- "name": "tasks_repository_id_repositories_id_fk",
- "tableFrom": "tasks",
- "tableTo": "repositories",
- "columnsFrom": [
- "repository_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "tasks_assignee_id_users_id_fk": {
- "name": "tasks_assignee_id_users_id_fk",
- "tableFrom": "tasks",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "assignee_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "set null",
- "onUpdate": "no action"
- },
- "tasks_creator_id_users_id_fk": {
- "name": "tasks_creator_id_users_id_fk",
- "tableFrom": "tasks",
- "tableTo": "users",
- "schemaTo": "auth",
- "columnsFrom": [
- "creator_id"
- ],
- "columnsTo": [
- "id"
- ],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "tasks_slug_unique": {
- "name": "tasks_slug_unique",
- "nullsNotDistinct": false,
- "columns": [
- "slug"
- ]
- },
- "tasks_external_unique": {
- "name": "tasks_external_unique",
- "nullsNotDistinct": false,
- "columns": [
- "external_provider",
- "external_id"
- ]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {
- "public.integration_provider": {
- "name": "integration_provider",
- "schema": "public",
- "values": [
- "linear"
- ]
- },
- "public.task_priority": {
- "name": "task_priority",
- "schema": "public",
- "values": [
- "urgent",
- "high",
- "medium",
- "low",
- "none"
- ]
- },
- "public.task_status": {
- "name": "task_status",
- "schema": "public",
- "values": [
- "backlog",
- "todo",
- "planning",
- "working",
- "needs-feedback",
- "ready-to-merge",
- "completed",
- "canceled"
- ]
- }
- },
- "schemas": {
- "auth": "auth",
- "ingest": "ingest"
- },
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json
index b6e6ceabf..af9d90179 100644
--- a/packages/db/drizzle/meta/_journal.json
+++ b/packages/db/drizzle/meta/_journal.json
@@ -36,27 +36,6 @@
"when": 1766710371092,
"tag": "0004_webhook_events_idempotency",
"breakpoints": true
- },
- {
- "idx": 5,
- "version": "7",
- "when": 1766942895219,
- "tag": "0005_back_up_user_tables",
- "breakpoints": true
- },
- {
- "idx": 6,
- "version": "7",
- "when": 1766991848204,
- "tag": "0006_add_better_auth_tables",
- "breakpoints": true
- },
- {
- "idx": 7,
- "version": "7",
- "when": 1767049518603,
- "tag": "0007_add_created_at_default",
- "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/db/package.json b/packages/db/package.json
index e8ccd3d9c..9ded8de44 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -20,10 +20,6 @@
"types": "./src/schema/index.ts",
"default": "./src/schema/index.ts"
},
- "./schema/auth": {
- "types": "./src/schema/auth.ts",
- "default": "./src/schema/auth.ts"
- },
"./utils": {
"types": "./src/utils/index.ts",
"default": "./src/utils/index.ts"
diff --git a/packages/db/src/env.ts b/packages/db/src/env.ts
index 2c51f729c..1ae470b29 100644
--- a/packages/db/src/env.ts
+++ b/packages/db/src/env.ts
@@ -12,12 +12,33 @@ export const env = createEnv({
DATABASE_URL_UNPOOLED: z.string().url(),
},
+ /**
+ * The prefix that client-side variables must have. This is enforced both at
+ * a type-level and at runtime.
+ */
clientPrefix: "PUBLIC_",
client: {},
+ /**
+ * What object holds the environment variables at runtime. This is usually
+ * `process.env` or `import.meta.env`.
+ */
runtimeEnv: process.env,
+ /**
+ * By default, this library will feed the environment variables directly to
+ * the Zod validator.
+ *
+ * This means that if you have an empty string for a value that is supposed
+ * to be a number (e.g. `PORT=` in a ".env" file), Zod will incorrectly flag
+ * it as a type mismatch violation. Additionally, if you have an empty string
+ * for a value that is supposed to be a string with a default value (e.g.
+ * `DOMAIN=` in an ".env" file), the default value will never be applied.
+ *
+ * In order to solve these issues, we recommend that all new projects
+ * explicitly specify this option as true.
+ */
emptyStringAsUndefined: true,
- skipValidation: true,
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
diff --git a/packages/db/src/schema/auth.ts b/packages/db/src/schema/auth.ts
deleted file mode 100644
index f810053e0..000000000
--- a/packages/db/src/schema/auth.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-import {
- boolean,
- index,
- pgSchema,
- text,
- timestamp,
- uniqueIndex,
- uuid,
-} from "drizzle-orm/pg-core";
-
-export const authSchema = pgSchema("auth");
-
-export const users = authSchema.table("users", {
- id: uuid("id").primaryKey().defaultRandom(),
- name: text("name").notNull(),
- email: text("email").notNull().unique(),
- emailVerified: boolean("email_verified").default(false).notNull(),
- image: text("image"),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- updatedAt: timestamp("updated_at")
- .defaultNow()
- .$onUpdate(() => new Date())
- .notNull(),
-});
-
-export type SelectUser = typeof users.$inferSelect;
-export type InsertUser = typeof users.$inferInsert;
-
-export const sessions = authSchema.table(
- "sessions",
- {
- id: uuid("id").primaryKey().defaultRandom(),
- expiresAt: timestamp("expires_at").notNull(),
- token: text("token").notNull().unique(),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- updatedAt: timestamp("updated_at")
- .$onUpdate(() => new Date())
- .notNull(),
- ipAddress: text("ip_address"),
- userAgent: text("user_agent"),
- userId: uuid("user_id")
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
- activeOrganizationId: uuid("active_organization_id"),
- },
- (table) => [index("sessions_user_id_idx").on(table.userId)],
-);
-
-export const accounts = authSchema.table(
- "accounts",
- {
- id: uuid("id").primaryKey().defaultRandom(),
- accountId: text("account_id").notNull(),
- providerId: text("provider_id").notNull(),
- userId: uuid("user_id")
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
- accessToken: text("access_token"),
- refreshToken: text("refresh_token"),
- idToken: text("id_token"),
- accessTokenExpiresAt: timestamp("access_token_expires_at"),
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
- scope: text("scope"),
- password: text("password"),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- updatedAt: timestamp("updated_at")
- .$onUpdate(() => new Date())
- .notNull(),
- },
- (table) => [index("accounts_user_id_idx").on(table.userId)],
-);
-
-export const verifications = authSchema.table(
- "verifications",
- {
- id: uuid("id").primaryKey().defaultRandom(),
- identifier: text("identifier").notNull(),
- value: text("value").notNull(),
- expiresAt: timestamp("expires_at").notNull(),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- updatedAt: timestamp("updated_at")
- .defaultNow()
- .$onUpdate(() => new Date())
- .notNull(),
- },
- (table) => [index("verifications_identifier_idx").on(table.identifier)],
-);
-
-export const organizations = authSchema.table(
- "organizations",
- {
- id: uuid("id").primaryKey().defaultRandom(),
- name: text("name").notNull(),
- slug: text("slug").notNull().unique(),
- logo: text("logo"),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- metadata: text("metadata"),
- },
- (table) => [uniqueIndex("organizations_slug_idx").on(table.slug)],
-);
-
-export type SelectOrganization = typeof organizations.$inferSelect;
-export type InsertOrganization = typeof organizations.$inferInsert;
-
-export const members = authSchema.table(
- "members",
- {
- id: uuid("id").primaryKey().defaultRandom(),
- organizationId: uuid("organization_id")
- .notNull()
- .references(() => organizations.id, { onDelete: "cascade" }),
- userId: uuid("user_id")
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
- role: text("role").default("member").notNull(),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- },
- (table) => [
- index("members_organization_id_idx").on(table.organizationId),
- index("members_user_id_idx").on(table.userId),
- ],
-);
-
-export type SelectMember = typeof members.$inferSelect;
-export type InsertMember = typeof members.$inferInsert;
-
-export const invitations = authSchema.table(
- "invitations",
- {
- id: uuid("id").primaryKey().defaultRandom(),
- organizationId: uuid("organization_id")
- .notNull()
- .references(() => organizations.id, { onDelete: "cascade" }),
- email: text("email").notNull(),
- role: text("role"),
- status: text("status").default("pending").notNull(),
- expiresAt: timestamp("expires_at").notNull(),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- inviterId: uuid("inviter_id")
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
- },
- (table) => [
- index("invitations_organization_id_idx").on(table.organizationId),
- index("invitations_email_idx").on(table.email),
- ],
-);
diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts
index 54196ae0b..aec9abc1f 100644
--- a/packages/db/src/schema/index.ts
+++ b/packages/db/src/schema/index.ts
@@ -1,4 +1,3 @@
-export * from "./auth";
export * from "./ingest";
export * from "./relations";
export * from "./schema";
diff --git a/packages/db/src/schema/relations.ts b/packages/db/src/schema/relations.ts
index 0528be976..07a4fd226 100644
--- a/packages/db/src/schema/relations.ts
+++ b/packages/db/src/schema/relations.ts
@@ -1,68 +1,41 @@
import { relations } from "drizzle-orm";
import {
- accounts,
- invitations,
- members,
+ integrationConnections,
+ organizationMembers,
organizations,
- sessions,
+ repositories,
+ tasks,
users,
-} from "./auth";
-import { integrationConnections, repositories, tasks } from "./schema";
+} from "./schema";
export const usersRelations = relations(users, ({ many }) => ({
- sessions: many(sessions),
- accounts: many(accounts),
- members: many(members),
- invitations: many(invitations),
+ organizationMembers: many(organizationMembers),
createdTasks: many(tasks, { relationName: "creator" }),
assignedTasks: many(tasks, { relationName: "assignee" }),
connectedIntegrations: many(integrationConnections),
}));
-export const sessionsRelations = relations(sessions, ({ one }) => ({
- user: one(users, {
- fields: [sessions.userId],
- references: [users.id],
- }),
-}));
-
-export const accountsRelations = relations(accounts, ({ one }) => ({
- user: one(users, {
- fields: [accounts.userId],
- references: [users.id],
- }),
-}));
-
export const organizationsRelations = relations(organizations, ({ many }) => ({
- members: many(members),
- invitations: many(invitations),
+ members: many(organizationMembers),
repositories: many(repositories),
tasks: many(tasks),
integrations: many(integrationConnections),
}));
-export const membersRelations = relations(members, ({ one }) => ({
- organization: one(organizations, {
- fields: [members.organizationId],
- references: [organizations.id],
- }),
- user: one(users, {
- fields: [members.userId],
- references: [users.id],
- }),
-}));
-
-export const invitationsRelations = relations(invitations, ({ one }) => ({
- organization: one(organizations, {
- fields: [invitations.organizationId],
- references: [organizations.id],
- }),
- inviter: one(users, {
- fields: [invitations.inviterId],
- references: [users.id],
+export const organizationMembersRelations = relations(
+ organizationMembers,
+ ({ one }) => ({
+ organization: one(organizations, {
+ fields: [organizationMembers.organizationId],
+ references: [organizations.id],
+ }),
+ user: one(users, {
+ fields: [organizationMembers.userId],
+ references: [users.id],
+ }),
}),
-}));
+);
export const repositoriesRelations = relations(
repositories,
diff --git a/packages/db/src/schema/schema.ts b/packages/db/src/schema/schema.ts
index c85b2d194..eed43d017 100644
--- a/packages/db/src/schema/schema.ts
+++ b/packages/db/src/schema/schema.ts
@@ -11,7 +11,6 @@ import {
uuid,
} from "drizzle-orm/pg-core";
-import { organizations, users } from "./auth";
import {
integrationProviderValues,
taskPriorityValues,
@@ -26,6 +25,81 @@ export const integrationProvider = pgEnum(
integrationProviderValues,
);
+export const users = pgTable(
+ "users",
+ {
+ id: uuid().primaryKey().defaultRandom(),
+ clerkId: text("clerk_id").notNull().unique(),
+ name: text().notNull(),
+ email: text().notNull().unique(),
+ avatarUrl: text("avatar_url"),
+ deletedAt: timestamp("deleted_at"),
+ createdAt: timestamp("created_at").notNull().defaultNow(),
+ updatedAt: timestamp("updated_at")
+ .notNull()
+ .defaultNow()
+ .$onUpdate(() => new Date()),
+ },
+ (table) => [
+ index("users_email_idx").on(table.email),
+ index("users_clerk_id_idx").on(table.clerkId),
+ index("users_deleted_at_idx").on(table.deletedAt),
+ ],
+);
+
+export type InsertUser = typeof users.$inferInsert;
+export type SelectUser = typeof users.$inferSelect;
+
+export const organizations = pgTable(
+ "organizations",
+ {
+ id: uuid().primaryKey().defaultRandom(),
+ clerkOrgId: text("clerk_org_id").unique(), // Clerk org ID - null until synced to Clerk
+ name: text().notNull(),
+ slug: text().notNull().unique(),
+ githubOrg: text("github_org"),
+ avatarUrl: text("avatar_url"),
+ createdAt: timestamp("created_at").notNull().defaultNow(),
+ updatedAt: timestamp("updated_at")
+ .notNull()
+ .defaultNow()
+ .$onUpdate(() => new Date()),
+ },
+ (table) => [
+ index("organizations_slug_idx").on(table.slug),
+ index("organizations_clerk_org_id_idx").on(table.clerkOrgId),
+ ],
+);
+
+export type InsertOrganization = typeof organizations.$inferInsert;
+export type SelectOrganization = typeof organizations.$inferSelect;
+
+export const organizationMembers = pgTable(
+ "organization_members",
+ {
+ id: uuid().primaryKey().defaultRandom(),
+ organizationId: uuid("organization_id")
+ .notNull()
+ .references(() => organizations.id, { onDelete: "cascade" }),
+ userId: uuid("user_id")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ role: text().notNull().default("member"), // "admin" | "member" | custom roles
+ createdAt: timestamp("created_at").notNull().defaultNow(),
+ },
+ (table) => [
+ index("organization_members_organization_id_idx").on(table.organizationId),
+ index("organization_members_user_id_idx").on(table.userId),
+ unique("organization_members_unique").on(
+ table.organizationId,
+ table.userId,
+ ),
+ ],
+);
+
+export type InsertOrganizationMember = typeof organizationMembers.$inferInsert;
+export type SelectOrganizationMember = typeof organizationMembers.$inferSelect;
+
export const repositories = pgTable(
"repositories",
{
@@ -76,7 +150,7 @@ export const tasks = pgTable(
.references(() => organizations.id, { onDelete: "cascade" }),
repositoryId: uuid("repository_id").references(() => repositories.id, {
onDelete: "cascade",
- }),
+ }), // Optional - Linear tasks won't have this
assigneeId: uuid("assignee_id").references(() => users.id, {
onDelete: "set null",
}),
diff --git a/packages/db/src/utils/sql.ts b/packages/db/src/utils/sql.ts
index 95d8ecd58..3628996b6 100644
--- a/packages/db/src/utils/sql.ts
+++ b/packages/db/src/utils/sql.ts
@@ -21,13 +21,7 @@ export async function getCurrentTxid(
tx: PgTransaction,
): Promise {
const result = await tx.execute<{ txid: string }>(
- sql`SELECT pg_current_xact_id()::text as txid`,
+ sql`SELECT txid_current()::text as txid`,
);
-
- const txid = result.rows[0]?.txid;
- if (!txid) {
- throw new Error("Failed to get current transaction ID");
- }
-
- return Number.parseInt(txid, 10);
+ return Number.parseInt(result.rows[0]?.txid ?? "", 10);
}
diff --git a/packages/local-db/drizzle/0004_add_terminal_link_behavior_setting.sql b/packages/local-db/drizzle/0004_add_terminal_link_behavior_setting.sql
new file mode 100644
index 000000000..ad70f21f3
--- /dev/null
+++ b/packages/local-db/drizzle/0004_add_terminal_link_behavior_setting.sql
@@ -0,0 +1 @@
+ALTER TABLE `settings` ADD `terminal_link_behavior` text;
\ No newline at end of file
diff --git a/packages/local-db/drizzle/0005_add_navigation_style.sql b/packages/local-db/drizzle/0005_add_navigation_style.sql
new file mode 100644
index 000000000..c3c175a03
--- /dev/null
+++ b/packages/local-db/drizzle/0005_add_navigation_style.sql
@@ -0,0 +1 @@
+ALTER TABLE `settings` ADD `navigation_style` text;
\ No newline at end of file
diff --git a/packages/local-db/drizzle/0006_add_unique_branch_workspace_index.sql b/packages/local-db/drizzle/0006_add_unique_branch_workspace_index.sql
new file mode 100644
index 000000000..ac308f5f3
--- /dev/null
+++ b/packages/local-db/drizzle/0006_add_unique_branch_workspace_index.sql
@@ -0,0 +1,47 @@
+-- Dedupe existing duplicate branch workspaces before creating unique index.
+-- Keep the most recently used one (highest last_opened_at), with id ASC as tiebreaker.
+-- First, update settings.last_active_workspace_id if it points to a workspace we're about to delete
+UPDATE settings
+SET last_active_workspace_id = (
+ SELECT w1.id FROM workspaces w1
+ WHERE w1.type = 'branch'
+ AND w1.project_id = (
+ SELECT w2.project_id FROM workspaces w2 WHERE w2.id = settings.last_active_workspace_id
+ )
+ ORDER BY w1.last_opened_at DESC NULLS LAST, w1.id ASC
+ LIMIT 1
+)
+WHERE last_active_workspace_id IN (
+ SELECT w1.id FROM workspaces w1
+ WHERE w1.type = 'branch'
+ AND EXISTS (
+ SELECT 1 FROM workspaces w2
+ WHERE w2.type = 'branch'
+ AND w2.project_id = w1.project_id
+ AND (
+ w2.last_opened_at > w1.last_opened_at
+ OR (w2.last_opened_at = w1.last_opened_at AND w2.id < w1.id)
+ OR (w2.last_opened_at IS NOT NULL AND w1.last_opened_at IS NULL)
+ OR (w1.last_opened_at IS NULL AND w2.last_opened_at IS NULL AND w2.id < w1.id)
+ )
+ )
+);
+--> statement-breakpoint
+-- Delete duplicate branch workspaces, keeping the most recently used per project
+-- Survivor selection: highest last_opened_at, then lowest id as tiebreaker
+DELETE FROM workspaces
+WHERE type = 'branch'
+AND id NOT IN (
+ SELECT id FROM (
+ SELECT id, ROW_NUMBER() OVER (
+ PARTITION BY project_id
+ ORDER BY last_opened_at DESC NULLS LAST, id ASC
+ ) as rn
+ FROM workspaces
+ WHERE type = 'branch'
+ ) ranked
+ WHERE rn = 1
+);
+--> statement-breakpoint
+-- Now safe to create the unique index
+CREATE UNIQUE INDEX IF NOT EXISTS `workspaces_unique_branch_per_project` ON `workspaces` (`project_id`) WHERE `type` = 'branch';
diff --git a/packages/local-db/drizzle/0007_add_workspace_is_unread.sql b/packages/local-db/drizzle/0007_add_workspace_is_unread.sql
new file mode 100644
index 000000000..9f3ca8ec3
--- /dev/null
+++ b/packages/local-db/drizzle/0007_add_workspace_is_unread.sql
@@ -0,0 +1 @@
+ALTER TABLE `workspaces` ADD `is_unread` integer DEFAULT false;
diff --git a/packages/local-db/drizzle/0008_add_group_tabs_position.sql b/packages/local-db/drizzle/0008_add_group_tabs_position.sql
new file mode 100644
index 000000000..2be007e2a
--- /dev/null
+++ b/packages/local-db/drizzle/0008_add_group_tabs_position.sql
@@ -0,0 +1 @@
+ALTER TABLE `settings` ADD `group_tabs_position` text;
diff --git a/packages/local-db/drizzle/meta/0004_snapshot.json b/packages/local-db/drizzle/meta/0004_snapshot.json
new file mode 100644
index 000000000..991b5469e
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0004_snapshot.json
@@ -0,0 +1,977 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "bb9f9f85-bcbb-4003-b20f-4c172a1c6fc8",
+ "prevId": "d5a52ac9-bc1e-4529-89bf-5748d4df5006",
+ "tables": {
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_app": {
+ "name": "last_used_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/0005_snapshot.json b/packages/local-db/drizzle/meta/0005_snapshot.json
new file mode 100644
index 000000000..14c02c328
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0005_snapshot.json
@@ -0,0 +1,984 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "ac200b80-657f-4cd7-b338-2d6adeb925e7",
+ "prevId": "bb9f9f85-bcbb-4003-b20f-4c172a1c6fc8",
+ "tables": {
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_app": {
+ "name": "last_used_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "navigation_style": {
+ "name": "navigation_style",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/0006_snapshot.json b/packages/local-db/drizzle/meta/0006_snapshot.json
new file mode 100644
index 000000000..5362480f6
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0006_snapshot.json
@@ -0,0 +1,984 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
+ "prevId": "ac200b80-657f-4cd7-b338-2d6adeb925e7",
+ "tables": {
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_app": {
+ "name": "last_used_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "navigation_style": {
+ "name": "navigation_style",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
diff --git a/packages/local-db/drizzle/meta/0007_snapshot.json b/packages/local-db/drizzle/meta/0007_snapshot.json
new file mode 100644
index 000000000..dbf24a697
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0007_snapshot.json
@@ -0,0 +1,992 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "a7b8c9d0-e1f2-3456-7890-abcdef123456",
+ "prevId": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
+ "tables": {
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_app": {
+ "name": "last_used_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "navigation_style": {
+ "name": "navigation_style",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_unread": {
+ "name": "is_unread",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
diff --git a/packages/local-db/drizzle/meta/0008_snapshot.json b/packages/local-db/drizzle/meta/0008_snapshot.json
new file mode 100644
index 000000000..3c3664f7a
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0008_snapshot.json
@@ -0,0 +1,999 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "160d23b9-a426-4e93-866a-3d6e6fc3c704",
+ "prevId": "a7b8c9d0-e1f2-3456-7890-abcdef123456",
+ "tables": {
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_app": {
+ "name": "last_used_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "navigation_style": {
+ "name": "navigation_style",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "group_tabs_position": {
+ "name": "group_tabs_position",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_unread": {
+ "name": "is_unread",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/_journal.json b/packages/local-db/drizzle/meta/_journal.json
index 3117a6e22..3b06ca4a7 100644
--- a/packages/local-db/drizzle/meta/_journal.json
+++ b/packages/local-db/drizzle/meta/_journal.json
@@ -29,6 +29,41 @@
"when": 1766932805546,
"tag": "0003_add_confirm_on_quit_setting",
"breakpoints": true
+ },
+ {
+ "idx": 4,
+ "version": "6",
+ "when": 1767166138761,
+ "tag": "0004_add_terminal_link_behavior_setting",
+ "breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "6",
+ "when": 1767166547886,
+ "tag": "0005_add_navigation_style",
+ "breakpoints": true
+ },
+ {
+ "idx": 6,
+ "version": "6",
+ "when": 1767230000000,
+ "tag": "0006_add_unique_branch_workspace_index",
+ "breakpoints": true
+ },
+ {
+ "idx": 7,
+ "version": "6",
+ "when": 1767350000000,
+ "tag": "0007_add_workspace_is_unread",
+ "breakpoints": true
+ },
+ {
+ "idx": 8,
+ "version": "6",
+ "when": 1767548339097,
+ "tag": "0008_add_group_tabs_position",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/local-db/src/schema/schema.ts b/packages/local-db/src/schema/schema.ts
index 9b0639805..4da25f609 100644
--- a/packages/local-db/src/schema/schema.ts
+++ b/packages/local-db/src/schema/schema.ts
@@ -5,6 +5,7 @@ import type {
ExternalApp,
GitHubStatus,
GitStatus,
+ TerminalLinkBehavior,
TerminalPreset,
WorkspaceType,
} from "./zod";
@@ -100,20 +101,24 @@ export const workspaces = sqliteTable(
lastOpenedAt: integer("last_opened_at")
.notNull()
.$defaultFn(() => Date.now()),
+ isUnread: integer("is_unread", { mode: "boolean" }).default(false),
},
(table) => [
index("workspaces_project_id_idx").on(table.projectId),
index("workspaces_worktree_id_idx").on(table.worktreeId),
index("workspaces_last_opened_at_idx").on(table.lastOpenedAt),
+ // NOTE: Migration 0006 creates an additional partial unique index:
+ // CREATE UNIQUE INDEX workspaces_unique_branch_per_project
+ // ON workspaces(project_id) WHERE type = 'branch'
+ // This enforces one branch workspace per project. Drizzle's schema DSL
+ // doesn't support partial/filtered indexes, so this constraint is only
+ // applied via the migration, not schema push. See migration 0006 for details.
],
);
export type InsertWorkspace = typeof workspaces.$inferInsert;
export type SelectWorkspace = typeof workspaces.$inferSelect;
-/**
- * Settings table - single row with typed columns
- */
export const settings = sqliteTable("settings", {
id: integer("id").primaryKey().default(1),
lastActiveWorkspaceId: text("last_active_workspace_id"),
@@ -127,6 +132,9 @@ export const settings = sqliteTable("settings", {
selectedRingtoneId: text("selected_ringtone_id"),
activeOrganizationId: text("active_organization_id"),
confirmOnQuit: integer("confirm_on_quit", { mode: "boolean" }),
+ terminalLinkBehavior: text(
+ "terminal_link_behavior",
+ ).$type(),
});
export type InsertSettings = typeof settings.$inferInsert;
diff --git a/packages/local-db/src/schema/zod.ts b/packages/local-db/src/schema/zod.ts
index bb33e1d15..ca8221e40 100644
--- a/packages/local-db/src/schema/zod.ts
+++ b/packages/local-db/src/schema/zod.ts
@@ -96,3 +96,13 @@ export const EXTERNAL_APPS = [
] as const;
export type ExternalApp = (typeof EXTERNAL_APPS)[number];
+
+/**
+ * Terminal link behavior options
+ */
+export const TERMINAL_LINK_BEHAVIORS = [
+ "external-editor",
+ "file-viewer",
+] as const;
+
+export type TerminalLinkBehavior = (typeof TERMINAL_LINK_BEHAVIORS)[number];
diff --git a/packages/trpc/package.json b/packages/trpc/package.json
index 238b65aee..3a1f8edca 100644
--- a/packages/trpc/package.json
+++ b/packages/trpc/package.json
@@ -18,8 +18,8 @@
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
+ "@clerk/backend": "^2.27.0",
"@linear/sdk": "^68.1.0",
- "@superset/auth": "workspace:*",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@t3-oss/env-core": "^0.13.8",
diff --git a/packages/trpc/src/env.ts b/packages/trpc/src/env.ts
index f799bc134..34600e60b 100644
--- a/packages/trpc/src/env.ts
+++ b/packages/trpc/src/env.ts
@@ -6,6 +6,7 @@ export const env = createEnv({
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
+ CLERK_SECRET_KEY: z.string().min(1),
BLOB_READ_WRITE_TOKEN: z.string().min(1),
POSTHOG_API_KEY: z.string(),
POSTHOG_API_HOST: z.string().url().default("https://us.posthog.com"),
diff --git a/packages/trpc/src/router/admin/admin.ts b/packages/trpc/src/router/admin/admin.ts
index bcc361405..c8bc2e956 100644
--- a/packages/trpc/src/router/admin/admin.ts
+++ b/packages/trpc/src/router/admin/admin.ts
@@ -1,23 +1,53 @@
import { db } from "@superset/db/client";
import { users } from "@superset/db/schema";
-import type { TRPCRouterRecord } from "@trpc/server";
-import { desc, eq } from "drizzle-orm";
+import { TRPCError, type TRPCRouterRecord } from "@trpc/server";
+import { desc, eq, isNotNull, isNull } from "drizzle-orm";
import { z } from "zod";
import { adminProcedure } from "../../trpc";
export const adminRouter = {
- listUsers: adminProcedure.query(() => {
+ listActiveUsers: adminProcedure.query(() => {
return db.query.users.findMany({
+ where: isNull(users.deletedAt),
orderBy: desc(users.createdAt),
});
}),
- deleteUser: adminProcedure
- .input(z.object({ userId: z.string() }))
+ listDeletedUsers: adminProcedure.query(() => {
+ return db.query.users.findMany({
+ where: isNotNull(users.deletedAt),
+ orderBy: desc(users.deletedAt),
+ });
+ }),
+
+ restoreUser: adminProcedure
+ .input(z.object({ userId: z.string().uuid() }))
.mutation(async ({ input }) => {
- // Delete user - Better Auth handles cascading session cleanup
- await db.delete(users).where(eq(users.id, input.userId));
- return { success: true };
+ const [user] = await db
+ .update(users)
+ .set({ deletedAt: null })
+ .where(eq(users.id, input.userId))
+ .returning();
+
+ if (!user) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "User not found",
+ });
+ }
+
+ return user;
+ }),
+
+ permanentlyDeleteUser: adminProcedure
+ .input(z.object({ userId: z.string().uuid() }))
+ .mutation(async () => {
+ // TODO: Implement Clerk user deletion, avatar cleanup, etc.
+ throw new TRPCError({
+ code: "NOT_IMPLEMENTED",
+ message:
+ "Permanent deletion not yet implemented - requires Clerk cleanup",
+ });
}),
} satisfies TRPCRouterRecord;
diff --git a/packages/trpc/src/router/analytics/analytics.ts b/packages/trpc/src/router/analytics/analytics.ts
index b996f2f41..11dc8796e 100644
--- a/packages/trpc/src/router/analytics/analytics.ts
+++ b/packages/trpc/src/router/analytics/analytics.ts
@@ -25,7 +25,7 @@ export interface LeaderboardEntry {
userId: string;
name: string;
email: string;
- image: string | null;
+ avatarUrl: string | null;
count: number;
}
@@ -255,7 +255,7 @@ export const analyticsRouter = {
userId: user.id,
name: user.name,
email: user.email,
- image: user.image,
+ avatarUrl: user.avatarUrl,
count,
};
})
diff --git a/packages/trpc/src/router/integration/integration.ts b/packages/trpc/src/router/integration/integration.ts
index 7d0ba16e5..56c05234a 100644
--- a/packages/trpc/src/router/integration/integration.ts
+++ b/packages/trpc/src/router/integration/integration.ts
@@ -1,5 +1,9 @@
import { db } from "@superset/db/client";
-import { integrationConnections, members } from "@superset/db/schema";
+import {
+ integrationConnections,
+ organizationMembers,
+ users,
+} from "@superset/db/schema";
import type { TRPCRouterRecord } from "@trpc/server";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
@@ -10,12 +14,19 @@ export const integrationRouter = {
linear: linearRouter,
list: protectedProcedure
- .input(z.object({ organizationId: z.uuid() }))
+ .input(z.object({ organizationId: z.string().uuid() }))
.query(async ({ ctx, input }) => {
- const membership = await db.query.members.findFirst({
+ const user = await db.query.users.findFirst({
+ where: eq(users.clerkId, ctx.userId),
+ });
+ if (!user) {
+ throw new Error("User not found");
+ }
+
+ const membership = await db.query.organizationMembers.findFirst({
where: and(
- eq(members.organizationId, input.organizationId),
- eq(members.userId, ctx.session.user.id),
+ eq(organizationMembers.organizationId, input.organizationId),
+ eq(organizationMembers.userId, user.id),
),
});
if (!membership) {
diff --git a/packages/trpc/src/router/integration/linear/linear.ts b/packages/trpc/src/router/integration/linear/linear.ts
index 47f43d48f..dc10cdb08 100644
--- a/packages/trpc/src/router/integration/linear/linear.ts
+++ b/packages/trpc/src/router/integration/linear/linear.ts
@@ -10,7 +10,7 @@ export const linearRouter = {
getConnection: protectedProcedure
.input(z.object({ organizationId: z.uuid() }))
.query(async ({ ctx, input }) => {
- await verifyOrgMembership(ctx.session.user.id, input.organizationId);
+ await verifyOrgMembership(ctx.userId, input.organizationId);
const connection = await db.query.integrationConnections.findFirst({
where: and(
eq(integrationConnections.organizationId, input.organizationId),
@@ -25,7 +25,7 @@ export const linearRouter = {
disconnect: protectedProcedure
.input(z.object({ organizationId: z.uuid() }))
.mutation(async ({ ctx, input }) => {
- await verifyOrgAdmin(ctx.session.user.id, input.organizationId);
+ await verifyOrgAdmin(ctx.userId, input.organizationId);
const result = await db
.delete(integrationConnections)
@@ -47,7 +47,7 @@ export const linearRouter = {
getTeams: protectedProcedure
.input(z.object({ organizationId: z.uuid() }))
.query(async ({ ctx, input }) => {
- await verifyOrgMembership(ctx.session.user.id, input.organizationId);
+ await verifyOrgMembership(ctx.userId, input.organizationId);
const client = await getLinearClient(input.organizationId);
if (!client) return [];
const teams = await client.teams();
@@ -62,7 +62,7 @@ export const linearRouter = {
}),
)
.mutation(async ({ ctx, input }) => {
- await verifyOrgAdmin(ctx.session.user.id, input.organizationId);
+ await verifyOrgAdmin(ctx.userId, input.organizationId);
const config: LinearConfig = {
provider: "linear",
diff --git a/packages/trpc/src/router/integration/linear/utils.ts b/packages/trpc/src/router/integration/linear/utils.ts
index 6afe04071..781a7ed5a 100644
--- a/packages/trpc/src/router/integration/linear/utils.ts
+++ b/packages/trpc/src/router/integration/linear/utils.ts
@@ -1,6 +1,10 @@
import { LinearClient } from "@linear/sdk";
import { db } from "@superset/db/client";
-import { integrationConnections, members } from "@superset/db/schema";
+import {
+ integrationConnections,
+ organizationMembers,
+ users,
+} from "@superset/db/schema";
import { and, eq } from "drizzle-orm";
type Priority = "urgent" | "high" | "medium" | "low" | "none";
@@ -53,13 +57,21 @@ export async function getLinearClient(
}
export async function verifyOrgMembership(
- userId: string,
+ clerkUserId: string,
organizationId: string,
) {
- const membership = await db.query.members.findFirst({
+ const user = await db.query.users.findFirst({
+ where: eq(users.clerkId, clerkUserId),
+ });
+
+ if (!user) {
+ throw new Error("User not found");
+ }
+
+ const membership = await db.query.organizationMembers.findFirst({
where: and(
- eq(members.organizationId, organizationId),
- eq(members.userId, userId),
+ eq(organizationMembers.organizationId, organizationId),
+ eq(organizationMembers.userId, user.id),
),
});
@@ -67,15 +79,21 @@ export async function verifyOrgMembership(
throw new Error("Not a member of this organization");
}
- return { membership };
+ return { user, membership };
}
-export async function verifyOrgAdmin(userId: string, organizationId: string) {
- const { membership } = await verifyOrgMembership(userId, organizationId);
+export async function verifyOrgAdmin(
+ clerkUserId: string,
+ organizationId: string,
+) {
+ const { user, membership } = await verifyOrgMembership(
+ clerkUserId,
+ organizationId,
+ );
- if (membership.role !== "admin" && membership.role !== "owner") {
+ if (membership.role !== "admin") {
throw new Error("Admin access required");
}
- return { membership };
+ return { user, membership };
}
diff --git a/packages/trpc/src/router/organization/organization.ts b/packages/trpc/src/router/organization/organization.ts
index 79adb907b..31057c1db 100644
--- a/packages/trpc/src/router/organization/organization.ts
+++ b/packages/trpc/src/router/organization/organization.ts
@@ -1,5 +1,5 @@
import { db } from "@superset/db/client";
-import { members, organizations } from "@superset/db/schema";
+import { organizationMembers, organizations, users } from "@superset/db/schema";
import type { TRPCRouterRecord } from "@trpc/server";
import { and, desc, eq } from "drizzle-orm";
import { z } from "zod";
@@ -52,24 +52,24 @@ export const organizationRouter = {
z.object({
name: z.string().min(1),
slug: z.string().min(1),
- logo: z.string().url().optional(),
+ githubOrg: z.string().optional(),
+ avatarUrl: z.string().url().optional(),
}),
)
.mutation(async ({ ctx, input }) => {
+ const user = await db.query.users.findFirst({
+ where: eq(users.clerkId, ctx.userId),
+ });
+
const [organization] = await db
.insert(organizations)
- .values({
- name: input.name,
- slug: input.slug,
- logo: input.logo,
- })
+ .values(input)
.returning();
- if (organization) {
- await db.insert(members).values({
+ if (user && organization) {
+ await db.insert(organizationMembers).values({
organizationId: organization.id,
- userId: ctx.session.user.id,
- role: "owner",
+ userId: user.id,
});
}
@@ -79,9 +79,10 @@ export const organizationRouter = {
update: protectedProcedure
.input(
z.object({
- id: z.string(),
+ id: z.string().uuid(),
name: z.string().min(1).optional(),
- logo: z.string().url().optional(),
+ githubOrg: z.string().optional(),
+ avatarUrl: z.string().url().optional(),
}),
)
.mutation(async ({ input }) => {
@@ -110,12 +111,8 @@ export const organizationRouter = {
)
.mutation(async ({ input }) => {
const [member] = await db
- .insert(members)
- .values({
- organizationId: input.organizationId,
- userId: input.userId,
- role: "member",
- })
+ .insert(organizationMembers)
+ .values(input)
.returning();
return member;
}),
@@ -129,11 +126,11 @@ export const organizationRouter = {
)
.mutation(async ({ input }) => {
await db
- .delete(members)
+ .delete(organizationMembers)
.where(
and(
- eq(members.organizationId, input.organizationId),
- eq(members.userId, input.userId),
+ eq(organizationMembers.organizationId, input.organizationId),
+ eq(organizationMembers.userId, input.userId),
),
);
return { success: true };
diff --git a/packages/trpc/src/router/task/task.ts b/packages/trpc/src/router/task/task.ts
index a99b6de76..da1d9b620 100644
--- a/packages/trpc/src/router/task/task.ts
+++ b/packages/trpc/src/router/task/task.ts
@@ -20,12 +20,12 @@ export const taskRouter = {
assignee: {
id: assignee.id,
name: assignee.name,
- image: assignee.image,
+ avatarUrl: assignee.avatarUrl,
},
creator: {
id: creator.id,
name: creator.name,
- image: creator.image,
+ avatarUrl: creator.avatarUrl,
},
})
.from(tasks)
@@ -74,12 +74,19 @@ export const taskRouter = {
create: protectedProcedure
.input(createTaskSchema)
.mutation(async ({ ctx, input }) => {
+ const [user] = await dbWs
+ .select()
+ .from(users)
+ .where(eq(users.clerkId, ctx.userId))
+ .limit(1);
+ if (!user) throw new Error("User not found");
+
const result = await dbWs.transaction(async (tx) => {
const [task] = await tx
.insert(tasks)
.values({
...input,
- creatorId: ctx.session.user.id,
+ creatorId: user.id,
labels: input.labels ?? [],
})
.returning();
diff --git a/packages/trpc/src/router/user/user.ts b/packages/trpc/src/router/user/user.ts
index 53aaaef10..d47cd4aa2 100644
--- a/packages/trpc/src/router/user/user.ts
+++ b/packages/trpc/src/router/user/user.ts
@@ -1,16 +1,38 @@
import { db } from "@superset/db/client";
-import { members } from "@superset/db/schema";
-import type { TRPCRouterRecord } from "@trpc/server";
+import { organizationMembers, users } from "@superset/db/schema";
+import { TRPCError, type TRPCRouterRecord } from "@trpc/server";
import { eq } from "drizzle-orm";
import { protectedProcedure } from "../../trpc";
+import { syncUserFromClerk } from "./utils/sync-user-from-clerk";
export const userRouter = {
- me: protectedProcedure.query(({ ctx }) => ctx.session.user),
+ me: protectedProcedure.query(async ({ ctx }) => {
+ const existingUser = await db.query.users.findFirst({
+ where: eq(users.clerkId, ctx.userId),
+ });
+
+ if (existingUser) {
+ return existingUser;
+ }
+
+ return syncUserFromClerk(ctx.userId);
+ }),
myOrganization: protectedProcedure.query(async ({ ctx }) => {
- const membership = await db.query.members.findFirst({
- where: eq(members.userId, ctx.session.user.id),
+ const user = await db.query.users.findFirst({
+ where: eq(users.clerkId, ctx.userId),
+ });
+
+ if (!user) {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: "User record not found",
+ });
+ }
+
+ const membership = await db.query.organizationMembers.findFirst({
+ where: eq(organizationMembers.userId, user.id),
with: {
organization: true,
},
@@ -20,8 +42,19 @@ export const userRouter = {
}),
myOrganizations: protectedProcedure.query(async ({ ctx }) => {
- const memberships = await db.query.members.findMany({
- where: eq(members.userId, ctx.session.user.id),
+ const user = await db.query.users.findFirst({
+ where: eq(users.clerkId, ctx.userId),
+ });
+
+ if (!user) {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: "User record not found",
+ });
+ }
+
+ const memberships = await db.query.organizationMembers.findMany({
+ where: eq(organizationMembers.userId, user.id),
with: {
organization: true,
},
diff --git a/packages/trpc/src/router/user/utils/sync-user-from-clerk.ts b/packages/trpc/src/router/user/utils/sync-user-from-clerk.ts
new file mode 100644
index 000000000..7b8b24193
--- /dev/null
+++ b/packages/trpc/src/router/user/utils/sync-user-from-clerk.ts
@@ -0,0 +1,83 @@
+import { createClerkClient } from "@clerk/backend";
+import { db } from "@superset/db/client";
+import { users } from "@superset/db/schema";
+import { put } from "@vercel/blob";
+import { eq } from "drizzle-orm";
+
+import { env } from "../../../env";
+
+const clerkClient = createClerkClient({
+ secretKey: env.CLERK_SECRET_KEY,
+});
+
+async function uploadAvatar(
+ imageUrl: string,
+ userId: string,
+): Promise {
+ try {
+ const response = await fetch(imageUrl);
+ if (!response.ok) return null;
+
+ const blob = await response.blob();
+ const { url } = await put(`users/${userId}/avatar.png`, blob, {
+ access: "public",
+ token: env.BLOB_READ_WRITE_TOKEN,
+ });
+ return url;
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Fetch user from Clerk and create in our database.
+ * Only called when user doesn't exist locally.
+ */
+export async function syncUserFromClerk(clerkId: string) {
+ const clerkUser = await clerkClient.users.getUser(clerkId);
+
+ const primaryEmail = clerkUser.emailAddresses.find(
+ (email) => email.id === clerkUser.primaryEmailAddressId,
+ )?.emailAddress;
+
+ if (!primaryEmail) {
+ return null;
+ }
+
+ const name =
+ [clerkUser.firstName, clerkUser.lastName].filter(Boolean).join(" ") ||
+ primaryEmail.split("@")[0] ||
+ "User";
+
+ // Upsert user - email is source of truth
+ const [user] = await db
+ .insert(users)
+ .values({
+ clerkId,
+ email: primaryEmail,
+ name,
+ })
+ .onConflictDoUpdate({
+ target: users.email,
+ set: {
+ clerkId,
+ name,
+ },
+ })
+ .returning();
+
+ if (!user) {
+ return null;
+ }
+
+ // Upload avatar if needed
+ if (!user.avatarUrl && clerkUser.imageUrl) {
+ const avatarUrl = await uploadAvatar(clerkUser.imageUrl, user.id);
+ if (avatarUrl) {
+ await db.update(users).set({ avatarUrl }).where(eq(users.id, user.id));
+ return { ...user, avatarUrl };
+ }
+ }
+
+ return user;
+}
diff --git a/packages/trpc/src/trpc.ts b/packages/trpc/src/trpc.ts
index 2842f07a0..8f0d1a0d5 100644
--- a/packages/trpc/src/trpc.ts
+++ b/packages/trpc/src/trpc.ts
@@ -1,14 +1,27 @@
-import type { Session } from "@superset/auth";
+import { db } from "@superset/db/client";
+import { users } from "@superset/db/schema";
import { COMPANY } from "@superset/shared/constants";
import { initTRPC, TRPCError } from "@trpc/server";
+import { eq } from "drizzle-orm";
import superjson from "superjson";
import { ZodError } from "zod";
+/**
+ * tRPC Context
+ *
+ * Simple auth context with just userId. Supports both:
+ * - Clerk sessions (web/admin via cookies)
+ * - Desktop auth (custom JWT tokens)
+ */
export type TRPCContext = {
- session: Session | null;
+ userId: string | null;
};
-export const createTRPCContext = (opts: TRPCContext): TRPCContext => opts;
+export const createTRPCContext = (opts: {
+ userId: string | null;
+}): TRPCContext => {
+ return { userId: opts.userId };
+};
const t = initTRPC.context().create({
transformer: superjson,
@@ -31,23 +44,42 @@ export const createCallerFactory = t.createCallerFactory;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
- if (!ctx.session) {
+ if (!ctx.userId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Not authenticated. Please sign in.",
});
}
- return next({ ctx: { session: ctx.session } });
+ return next({
+ ctx: {
+ userId: ctx.userId,
+ },
+ });
});
export const adminProcedure = protectedProcedure.use(async ({ ctx, next }) => {
- if (!ctx.session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
+ const user = await db.query.users.findFirst({
+ where: eq(users.clerkId, ctx.userId),
+ });
+
+ if (!user) {
+ throw new TRPCError({
+ code: "UNAUTHORIZED",
+ message: "User not found in database.",
+ });
+ }
+
+ if (!user.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
throw new TRPCError({
code: "FORBIDDEN",
message: `Admin access requires ${COMPANY.EMAIL_DOMAIN} email.`,
});
}
- return next({ ctx });
+ return next({
+ ctx: {
+ user,
+ },
+ });
});