diff --git a/apps/desktop/package.json b/apps/desktop/package.json index efc3e8bf960..c736f02d3cb 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -45,6 +45,7 @@ "dotenv": "^17.2.3", "electron-router-dom": "^2.1.0", "electron-store": "^11.0.2", + "execa": "^9.6.0", "fast-glob": "^3.3.3", "framer-motion": "^12.23.24", "http-proxy": "^1.18.1", @@ -61,6 +62,7 @@ "react-resizable-panels": "^3.0.6", "react-router-dom": "^7.8.2", "react-syntax-highlighter": "^16.1.0", + "simple-git": "^3.30.0", "superjson": "^2.2.5", "tailwind-merge": "^2.6.0", "trpc-electron": "^0.1.2", diff --git a/apps/desktop/src/lib/trpc/routers/projects/projects.ts b/apps/desktop/src/lib/trpc/routers/projects/projects.ts index 84604fd27f1..fa2275fd687 100644 --- a/apps/desktop/src/lib/trpc/routers/projects/projects.ts +++ b/apps/desktop/src/lib/trpc/routers/projects/projects.ts @@ -1,90 +1,74 @@ import { dialog } from "electron"; import type { BrowserWindow } from "electron"; import { basename } from "node:path"; -import { z } from "zod"; +import { nanoid } from "nanoid"; import { publicProcedure, router } from "../.."; import { db } from "../../../../main/lib/db"; -import type { RecentProject } from "../../../../main/lib/db/schemas"; +import type { Project } from "../../../../main/lib/db/schemas"; +import { getGitRoot } from "../workspaces/utils/git"; -/** - * Projects router - * Handles project selection, recents management, and workspace creation - */ export const createProjectsRouter = (window: BrowserWindow) => { return router({ - /** - * Open a new project via folder picker - * Adds to recents and returns path for UI to handle - */ - openProject: publicProcedure.mutation(async () => { + getRecents: publicProcedure.query((): Project[] => { + return db.data.projects + .slice() + .sort((a, b) => b.lastOpenedAt - a.lastOpenedAt); + }), + + openNew: publicProcedure.mutation(async () => { const result = await dialog.showOpenDialog(window, { properties: ["openDirectory"], title: "Open Project", }); if (result.canceled || result.filePaths.length === 0) { - return { success: false as const }; + return { success: false }; } - const path = result.filePaths[0]; - const name = basename(path); + const selectedPath = result.filePaths[0]; - await db.update((data) => { - const existingIndex = data.recentProjects.findIndex( - (p) => p.path === path, - ); - if (existingIndex !== -1) { - data.recentProjects[existingIndex].lastOpenedAt = Date.now(); - } else { - data.recentProjects.push({ - path, - name, - lastOpenedAt: Date.now(), - }); - } - }); + let mainRepoPath: string; + try { + mainRepoPath = await getGitRoot(selectedPath); + } catch (_error) { + return { + success: false, + error: "Selected folder is not in a git repository", + }; + } - return { - success: true as const, - path, - name, - }; - }), - openRecent: publicProcedure - .input(z.object({ path: z.string() })) - .mutation(async ({ input }) => { - const { path } = input; - const name = basename(path); + const name = basename(mainRepoPath); + let project = db.data.projects.find( + (p) => p.mainRepoPath === mainRepoPath, + ); + + if (project) { await db.update((data) => { - const recent = data.recentProjects.find((p) => p.path === path); - if (recent) { - recent.lastOpenedAt = Date.now(); + const p = data.projects.find((p) => p.id === project?.id); + if (p) { + p.lastOpenedAt = Date.now(); } }); - - return { - success: true as const, - path, + } else { + project = { + id: nanoid(), + mainRepoPath, name, + lastOpenedAt: Date.now(), + createdAt: Date.now(), }; - }), - getRecents: publicProcedure.query((): RecentProject[] => { - return db.data.recentProjects - .slice() - .sort((a, b) => b.lastOpenedAt - a.lastOpenedAt); - }), - removeRecent: publicProcedure - .input(z.object({ path: z.string() })) - .mutation(async ({ input }) => { + await db.update((data) => { - data.recentProjects = data.recentProjects.filter( - (p) => p.path !== input.path, - ); + data.projects.push(project!); }); + } - return { success: true }; - }), + return { + success: true as const, + project, + }; + }), }); }; diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts b/apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts new file mode 100644 index 00000000000..52448d7be9f --- /dev/null +++ b/apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts @@ -0,0 +1,91 @@ +import simpleGit from "simple-git"; +import { join } from "node:path"; +import { mkdir } from "node:fs/promises"; + +export function generateBranchName(): string { + const adjectives = [ + "azure", + "crimson", + "emerald", + "golden", + "indigo", + "jade", + "lavender", + "magenta", + "navy", + "olive", + "pearl", + "rose", + "silver", + "teal", + "violet", + ]; + + const nouns = [ + "cloud", + "forest", + "mountain", + "ocean", + "river", + "storm", + "sunset", + "thunder", + "wave", + "wind", + "meadow", + "canyon", + "glacier", + "valley", + "peak", + ]; + + const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; + const noun = nouns[Math.floor(Math.random() * nouns.length)]; + const number = Math.floor(Math.random() * 100); + + return `${adjective}-${noun}-${number}`; +} + +export async function createWorktree( + mainRepoPath: string, + branch: string, + worktreePath: string, +): Promise { + try { + const parentDir = join(worktreePath, ".."); + await mkdir(parentDir, { recursive: true }); + + const git = simpleGit(mainRepoPath); + await git.raw(["worktree", "add", worktreePath, "-b", branch]); + + console.log(`Created worktree at ${worktreePath} with branch ${branch}`); + } catch (error) { + console.error(`Failed to create worktree: ${error}`); + throw new Error(`Failed to create worktree: ${error}`); + } +} + +export async function removeWorktree( + mainRepoPath: string, + worktreePath: string, +): Promise { + try { + const git = simpleGit(mainRepoPath); + await git.raw(["worktree", "remove", worktreePath, "--force"]); + + console.log(`Removed worktree at ${worktreePath}`); + } catch (error) { + console.error(`Failed to remove worktree: ${error}`); + throw new Error(`Failed to remove worktree: ${error}`); + } +} + +export async function getGitRoot(path: string): Promise { + try { + const git = simpleGit(path); + const root = await git.revparse(["--show-toplevel"]); + return root.trim(); + } catch (error) { + throw new Error(`Not a git repository: ${path}`); + } +} diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts b/apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts index 467d0ca5673..312c6ca0a20 100644 --- a/apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts +++ b/apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts @@ -1,43 +1,78 @@ import { z } from "zod"; import { nanoid } from "nanoid"; +import { join } from "node:path"; import { publicProcedure, router } from "../.."; import { db } from "../../../../main/lib/db"; +import { + generateBranchName, + createWorktree, + removeWorktree, +} from "./utils/git"; -/** - * Workspaces router - * Handles workspace CRUD operations - */ export const createWorkspacesRouter = () => { return router({ - /** - * Create a new workspace - */ create: publicProcedure .input( z.object({ - name: z.string(), - path: z.string().nullable().optional(), + projectId: z.string(), + name: z.string().optional(), }), ) .mutation(async ({ input }) => { + // Find the project + const project = db.data.projects.find((p) => p.id === input.projectId); + if (!project) { + throw new Error(`Project ${input.projectId} not found`); + } + + const branch = generateBranchName(); + + const worktreePath = join( + project.mainRepoPath, + ".superset", + branch, + ); + + // Create git worktree + await createWorktree(project.mainRepoPath, branch, worktreePath); + + // Create worktree record + const worktree = { + id: nanoid(), + projectId: input.projectId, + path: worktreePath, + branch, + createdAt: Date.now(), + }; + // Set order to be at the end of the list - const maxOrder = db.data.workspaces.length > 0 - ? Math.max(...db.data.workspaces.map((w) => w.order)) - : -1; + const maxOrder = + db.data.workspaces.length > 0 + ? Math.max(...db.data.workspaces.map((w) => w.order)) + : -1; const workspace = { id: nanoid(), - name: input.name, - path: input.path ?? null, + projectId: input.projectId, + worktreeId: worktree.id, + name: input.name ?? branch, order: maxOrder + 1, createdAt: Date.now(), updatedAt: Date.now(), lastOpenedAt: Date.now(), }; + // Save to database await db.update((data) => { + data.worktrees.push(worktree); data.workspaces.push(workspace); data.settings.lastActiveWorkspaceId = workspace.id; + + // Update project lastOpenedAt + const p = data.projects.find((p) => p.id === input.projectId); + if (p) { + p.lastOpenedAt = Date.now(); + } }); return workspace; @@ -45,12 +80,16 @@ export const createWorkspacesRouter = () => { /** * Get a workspace by ID + * Throws if workspace not found */ get: publicProcedure .input(z.object({ id: z.string() })) .query(({ input }) => { const workspace = db.data.workspaces.find((w) => w.id === input.id); - return workspace || null; + if (!workspace) { + throw new Error(`Workspace ${input.id} not found`); + } + return workspace; }), /** @@ -64,6 +103,8 @@ export const createWorkspacesRouter = () => { /** * Get the last active workspace + * Returns null if no active workspace set (valid state) + * Throws if active workspace ID exists but workspace not found (data inconsistency) */ getActive: publicProcedure.query(() => { const { lastActiveWorkspaceId } = db.data.settings; @@ -72,7 +113,16 @@ export const createWorkspacesRouter = () => { return null; } - return db.data.workspaces.find((w) => w.id === lastActiveWorkspaceId) || null; + const workspace = db.data.workspaces.find( + (w) => w.id === lastActiveWorkspaceId, + ); + if (!workspace) { + throw new Error( + `Active workspace ${lastActiveWorkspaceId} not found in database`, + ); + } + + return workspace; }), /** @@ -85,7 +135,6 @@ export const createWorkspacesRouter = () => { id: z.string(), patch: z.object({ name: z.string().optional(), - path: z.string().nullable().optional(), }), }), ) @@ -100,9 +149,6 @@ export const createWorkspacesRouter = () => { if (input.patch.name !== undefined) { workspace.name = input.patch.name; } - if (input.patch.path !== undefined) { - workspace.path = input.patch.path; - } // Update timestamps workspace.updatedAt = Date.now(); @@ -113,8 +159,7 @@ export const createWorkspacesRouter = () => { }), /** - * Delete a workspace - * Also removes from recents if no other workspace uses that path + * Delete a workspace and its associated worktree */ delete: publicProcedure .input(z.object({ id: z.string() })) @@ -125,21 +170,33 @@ export const createWorkspacesRouter = () => { return { success: false, error: "Workspace not found" }; } - const workspacePath = workspace.path; + // Find associated worktree and project + const worktree = db.data.worktrees.find( + (wt) => wt.id === workspace.worktreeId, + ); + const project = db.data.projects.find( + (p) => p.id === workspace.projectId, + ); + // Remove git worktree if it exists + if (worktree && project) { + try { + await removeWorktree(project.mainRepoPath, worktree.path); + } catch (error) { + console.error("Failed to remove worktree:", error); + // Continue with database cleanup even if git operation fails + } + } + + // Remove from database await db.update((data) => { // Remove workspace data.workspaces = data.workspaces.filter((w) => w.id !== input.id); - // Check if any other workspace uses this path - const otherWorkspaceWithSamePath = data.workspaces.some( - (w) => w.path === workspacePath, - ); - - // If no other workspace uses this path, remove from recents - if (!otherWorkspaceWithSamePath) { - data.recentProjects = data.recentProjects.filter( - (p) => p.path !== workspacePath, + // Remove worktree + if (worktree) { + data.worktrees = data.worktrees.filter( + (wt) => wt.id !== worktree.id, ); } diff --git a/apps/desktop/src/main/lib/db/schemas.ts b/apps/desktop/src/main/lib/db/schemas.ts index 8964461332a..2a5df8ebf7a 100644 --- a/apps/desktop/src/main/lib/db/schemas.ts +++ b/apps/desktop/src/main/lib/db/schemas.ts @@ -3,38 +3,59 @@ * These types define the structure of data stored in lowdb */ -export interface RecentProject { - path: string; - name: string; - lastOpenedAt: number; +/** + * Project represents a main git repository + */ +export interface Project { + id: string; // nanoid + mainRepoPath: string; // Absolute path to the main git repo + name: string; // Project name (derived from folder name) + lastOpenedAt: number; // Timestamp of last access + createdAt: number; } -export interface Tab { +/** + * Worktree represents a git worktree + */ +export interface Worktree { id: string; // nanoid - title: string; - terminalId?: string; - type: "single" | "group"; + projectId: string; // References Project.id + path: string; // Absolute path to the worktree + branch: string; // Git branch name - source of truth for git operations createdAt: number; - updatedAt: number; } +/** + * Workspace represents a UI tab (1:1 with Worktree) + */ export interface Workspace { id: string; // nanoid - path: string | null; // null for new workspaces that haven't opened a project yet - name: string; + projectId: string; // References Project.id + worktreeId: string; // References Worktree.id + name: string; // User-facing workspace name order: number; // Explicit order in the workspace tabs (0 = first, 1 = second, etc.) createdAt: number; updatedAt: number; lastOpenedAt: number; } +export interface Tab { + id: string; // nanoid + title: string; + terminalId?: string; + type: "single" | "group"; + createdAt: number; + updatedAt: number; +} + export interface Settings { lastActiveWorkspaceId?: string; } export interface Database { + projects: Project[]; + worktrees: Worktree[]; workspaces: Workspace[]; - recentProjects: RecentProject[]; settings: Settings; } @@ -42,7 +63,8 @@ export interface Database { * Default database state */ export const defaultDatabase: Database = { + projects: [], + worktrees: [], workspaces: [], - recentProjects: [], settings: {}, }; diff --git a/apps/desktop/src/renderer/hooks/useGlobalShortcuts.ts b/apps/desktop/src/renderer/hooks/useGlobalShortcuts.ts index 78704a592fa..aab69090626 100644 --- a/apps/desktop/src/renderer/hooks/useGlobalShortcuts.ts +++ b/apps/desktop/src/renderer/hooks/useGlobalShortcuts.ts @@ -15,7 +15,8 @@ import { useSplitTabVertical, useTabs, } from "../stores/tabs"; -import { useWorkspacesStore } from "../stores/workspaces"; +import { trpc } from "../lib/trpc"; +import { useSetActiveWorkspace } from "../react-query/workspaces"; function findWorkspaceIndex( workspaces: Array<{ id: string }>, @@ -31,8 +32,9 @@ function findTabIndex(tabs: Array<{ id: string }>, id: string | null) { } export function useGlobalShortcuts() { - const { workspaces, activeWorkspaceId, setActiveWorkspace } = - useWorkspacesStore(); + const { data: workspaces = [] } = trpc.workspaces.getAll.useQuery(); + const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); + const setActiveWorkspace = useSetActiveWorkspace(); const { toggleSidebar } = useSidebarStore(); const tabs = useTabs(); const activeTabIds = useActiveTabIds(); @@ -42,6 +44,8 @@ export function useGlobalShortcuts() { const splitTabVertical = useSplitTabVertical(); const splitTabHorizontal = useSplitTabHorizontal(); + const activeWorkspaceId = activeWorkspace?.id; + const workspaceTabs = useMemo(() => { if (!activeWorkspaceId) return []; return tabs.filter( @@ -59,14 +63,14 @@ export function useGlobalShortcuts() { if (!activeWorkspaceId) return; const index = findWorkspaceIndex(workspaces, activeWorkspaceId); if (index > 0) { - setActiveWorkspace(workspaces[index - 1].id); + setActiveWorkspace.mutate({ id: workspaces[index - 1].id }); } }, switchToNextWorkspace: () => { if (!activeWorkspaceId) return; const index = findWorkspaceIndex(workspaces, activeWorkspaceId); if (index < workspaces.length - 1) { - setActiveWorkspace(workspaces[index + 1].id); + setActiveWorkspace.mutate({ id: workspaces[index + 1].id }); } }, toggleSidebar, diff --git a/apps/desktop/src/renderer/react-query/projects/index.ts b/apps/desktop/src/renderer/react-query/projects/index.ts index 1d552caa22f..8f1df6c0302 100644 --- a/apps/desktop/src/renderer/react-query/projects/index.ts +++ b/apps/desktop/src/renderer/react-query/projects/index.ts @@ -1,3 +1 @@ -export { useOpenProject } from "./useOpenProject"; -export { useOpenRecent } from "./useOpenRecent"; -export { useRemoveRecent } from "./useRemoveRecent"; +export { useOpenNew } from "./useOpenNew"; diff --git a/apps/desktop/src/renderer/react-query/projects/useOpenNew.ts b/apps/desktop/src/renderer/react-query/projects/useOpenNew.ts new file mode 100644 index 00000000000..3dc7e1b92e0 --- /dev/null +++ b/apps/desktop/src/renderer/react-query/projects/useOpenNew.ts @@ -0,0 +1,22 @@ +import { trpc } from "renderer/lib/trpc"; + +/** + * Mutation hook for opening a new project + * Creates a Project record if it doesn't exist + */ +export function useOpenNew( + options?: Parameters[0], +) { + const utils = trpc.useUtils(); + + return trpc.projects.openNew.useMutation({ + ...options, + onSuccess: async (...args) => { + // Auto-invalidate projects query + await utils.projects.getRecents.invalidate(); + + // Call user's onSuccess if provided + await options?.onSuccess?.(...args); + }, + }); +} diff --git a/apps/desktop/src/renderer/react-query/projects/useOpenProject.ts b/apps/desktop/src/renderer/react-query/projects/useOpenProject.ts deleted file mode 100644 index 67e08ef4689..00000000000 --- a/apps/desktop/src/renderer/react-query/projects/useOpenProject.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { trpc } from "renderer/lib/trpc"; - -/** - * Mutation hook for opening a new project - * Automatically invalidates workspace queries on success - */ -export function useOpenProject( - options?: Parameters[0], -) { - const utils = trpc.useUtils(); - - return trpc.projects.openProject.useMutation({ - ...options, - onSuccess: async (...args) => { - // Auto-invalidate workspace queries - await utils.workspaces.invalidate(); - - // Call user's onSuccess if provided - await options?.onSuccess?.(...args); - }, - }); -} diff --git a/apps/desktop/src/renderer/react-query/projects/useOpenRecent.ts b/apps/desktop/src/renderer/react-query/projects/useOpenRecent.ts deleted file mode 100644 index 2fdfadfb43e..00000000000 --- a/apps/desktop/src/renderer/react-query/projects/useOpenRecent.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { trpc } from "renderer/lib/trpc"; - -/** - * Mutation hook for opening a recent project - * Automatically invalidates workspace queries on success - */ -export function useOpenRecent( - options?: Parameters[0], -) { - const utils = trpc.useUtils(); - - return trpc.projects.openRecent.useMutation({ - ...options, - onSuccess: async (...args) => { - // Auto-invalidate workspace queries - await utils.workspaces.invalidate(); - - // Call user's onSuccess if provided - await options?.onSuccess?.(...args); - }, - }); -} diff --git a/apps/desktop/src/renderer/react-query/projects/useRemoveRecent.ts b/apps/desktop/src/renderer/react-query/projects/useRemoveRecent.ts deleted file mode 100644 index 052ee20ab54..00000000000 --- a/apps/desktop/src/renderer/react-query/projects/useRemoveRecent.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { trpc } from "renderer/lib/trpc"; - -/** - * Mutation hook for removing a recent project - * Automatically invalidates recent projects query on success - */ -export function useRemoveRecent( - options?: Parameters[0], -) { - const utils = trpc.useUtils(); - - return trpc.projects.removeRecent.useMutation({ - ...options, - onSuccess: async (...args) => { - // Auto-invalidate recent projects query - await utils.projects.getRecents.invalidate(); - - // Call user's onSuccess if provided - await options?.onSuccess?.(...args); - }, - }); -} diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/AddWorkspaceButton.tsx b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/AddWorkspaceButton.tsx deleted file mode 100644 index 6ac13570941..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/AddWorkspaceButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Button } from "@superset/ui/button"; -import { useCreateWorkspace } from "renderer/react-query/workspaces"; - -export function AddWorkspaceButton() { - const createWorkspace = useCreateWorkspace(); - - const handleAddWorkspace = () => { - createWorkspace.mutate({ - name: "New Workspace", - }); - }; - - return ( - - ); -} diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx new file mode 100644 index 00000000000..bf0f9d3a717 --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx @@ -0,0 +1,90 @@ +import { useState } from "react"; +import { Button } from "@superset/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from "@superset/ui/dropdown-menu"; +import { FolderOpen } from "lucide-react"; +import { trpc } from "renderer/lib/trpc"; +import { useCreateWorkspace } from "renderer/react-query/workspaces"; +import { useOpenNew } from "renderer/react-query/projects"; + +export interface WorkspaceDropdownProps { + className?: string; +} + +export function WorkspaceDropdown({ className }: WorkspaceDropdownProps) { + const [isOpen, setIsOpen] = useState(false); + + const { data: recentProjects = [] } = trpc.projects.getRecents.useQuery(); + const createWorkspace = useCreateWorkspace(); + const openNew = useOpenNew(); + + const handleCreateWorkspace = (projectId: string) => { + createWorkspace.mutate( + { projectId }, + { + onSuccess: () => { + setIsOpen(false); + }, + }, + ); + }; + + const handleOpenNewProject = () => { + openNew.mutate(undefined, { + onSuccess: (result) => { + if (result.success && result.project) { + handleCreateWorkspace(result.project.id); + } + }, + }); + }; + + return ( + + + + + +
+ {recentProjects.length > 0 && ( +
+

+ Recent Projects +

+ {recentProjects.map((project) => ( + + ))} +
+ )} +
+ +
+
+
+
+ ); +} diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx index 4dd9c437995..6e720cd311d 100644 --- a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx +++ b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx @@ -59,7 +59,7 @@ export function WorkspaceItem({ return (
{/* Main workspace button */} diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx index 0633807e154..9687f5f62da 100644 --- a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx @@ -1,7 +1,7 @@ import { Separator } from "@superset/ui/separator"; import { Fragment, useEffect, useRef, useState } from "react"; import { trpc } from "renderer/lib/trpc"; -import { AddWorkspaceButton } from "./AddWorkspaceButton"; +import { WorkspaceDropdown } from "./WorkspaceDropdown"; import { WorkspaceItem } from "./WorkspaceItem"; const MIN_WORKSPACE_WIDTH = 60; @@ -66,9 +66,8 @@ export function WorkspacesTabs() {
-
+
)}
+ -
- -
); } diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/index.tsx b/apps/desktop/src/renderer/screens/main/components/TopBar/index.tsx index 735bdce15ad..1f1d4d3360e 100644 --- a/apps/desktop/src/renderer/screens/main/components/TopBar/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/TopBar/index.tsx @@ -19,7 +19,7 @@ export function TopBar() {
-
+
{!isMac && }
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 2053e84d43a..abc1ab236b5 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 @@ -3,8 +3,8 @@ import { TabType, useActiveTabIds, useTabs, - useWorkspacesStore, } from "renderer/stores"; +import { trpc } from "renderer/lib/trpc"; import { EmptyTabView } from "./EmptyTabView"; import { GroupTabView } from "./GroupTabView"; import { SingleTabView } from "./SingleTabView"; @@ -12,9 +12,8 @@ import { useTabContentDrop } from "./useTabContentDrop"; import { DropOverlay } from "./DropOverlay"; export function TabsContent() { - const activeWorkspaceId = useWorkspacesStore( - (state) => state.activeWorkspaceId, - ); + const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); + const activeWorkspaceId = activeWorkspace?.id; const allTabs = useTabs(); const activeTabIds = useActiveTabIds(); diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView.tsx deleted file mode 100644 index 415d04c7974..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { trpc } from "renderer/lib/trpc"; -import { useAddTab } from "renderer/stores"; -import { useOpenProject, useOpenRecent, useRemoveRecent } from "renderer/react-query/projects"; -import { useUpdateWorkspace } from "renderer/react-query/workspaces"; -import { StartSection } from "./NewWorkspaceView/components/StartSection"; -import { RecentSection } from "./NewWorkspaceView/components/RecentSection"; - -export function NewWorkspaceView() { - const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); - const addTab = useAddTab(); - - const { data: recents = [] } = trpc.projects.getRecents.useQuery(); - - const updateWorkspace = useUpdateWorkspace(); - - const openProject = useOpenProject({ - onSuccess: async (result) => { - if (result.success && activeWorkspace) { - // Update workspace in DB with path and wait for it to complete - await updateWorkspace.mutateAsync({ - id: activeWorkspace.id, - patch: { - path: result.path, - name: result.name, - }, - }); - - // Add a tab for the project (still using Zustand for now) - addTab(activeWorkspace.id); - } - }, - }); - - const openRecent = useOpenRecent({ - onSuccess: async (result) => { - if (result.success && activeWorkspace) { - // Update workspace in DB with path and wait for it to complete - await updateWorkspace.mutateAsync({ - id: activeWorkspace.id, - patch: { - path: result.path, - name: result.name, - }, - }); - - // Add a tab for the project (still using Zustand for now) - addTab(activeWorkspace.id); - } - }, - }); - - const removeRecent = useRemoveRecent(); - - const handleOpenProject = () => { - openProject.mutate(); - }; - - const handleOpenRecent = (path: string) => { - openRecent.mutate({ path }); - }; - - const handleRemoveRecent = (path: string) => { - removeRecent.mutate({ path }); - }; - - return ( -
- {/* Left column - Start and Recent sections */} -
-
-

- Welcome to Superset -

-

- Open a project to get started -

-
- -
- - - -
-
- - {/* Right column - Placeholder for future content */} -
-
-

- Quick actions and walkthroughs will appear here -

-
-
-
- ); -} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/RecentProjectItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/RecentProjectItem.tsx deleted file mode 100644 index 646922d7be5..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/RecentProjectItem.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Folder, X } from "lucide-react"; -import { Button } from "@superset/ui/button"; -import type { RecentProject } from "shared/types"; - -interface RecentProjectItemProps { - project: RecentProject; - onOpen: (path: string) => void; - onRemove: (path: string) => void; -} - -function formatTimestamp(timestamp: number): string { - const now = Date.now(); - const diff = now - timestamp; - - const seconds = Math.floor(diff / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); - - if (days > 0) { - return days === 1 ? "Yesterday" : `${days} days ago`; - } - if (hours > 0) { - return `${hours} hour${hours > 1 ? "s" : ""} ago`; - } - if (minutes > 0) { - return `${minutes} minute${minutes > 1 ? "s" : ""} ago`; - } - return "Just now"; -} - -export function RecentProjectItem({ - project, - onOpen, - onRemove, -}: RecentProjectItemProps) { - return ( - -
- - ); -} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/RecentSection.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/RecentSection.tsx deleted file mode 100644 index 9a0f2e7212e..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/RecentSection.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { ScrollArea } from "@superset/ui/scroll-area"; - -import { RecentProjectItem } from "./RecentProjectItem"; -import type { RecentProject } from "shared/types"; - -interface RecentSectionProps { - recents: RecentProject[]; - onOpenRecent: (path: string) => void; - onRemoveRecent: (path: string) => void; -} - -export function RecentSection({ - recents, - onOpenRecent, - onRemoveRecent, -}: RecentSectionProps) { - if (recents.length === 0) { - return null; - } - - return ( -
-

Recent

- -
- {recents.map((project) => ( - - ))} -
-
-
- ); -} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/index.ts deleted file mode 100644 index 7d5fdc9d9e6..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/RecentSection/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { RecentSection } from "./RecentSection"; -export { RecentProjectItem } from "./RecentProjectItem"; diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/StartSection/StartSection.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/StartSection/StartSection.tsx deleted file mode 100644 index ac1977c9413..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/StartSection/StartSection.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Button } from "@superset/ui/button"; -import { FolderOpen } from "lucide-react"; - -interface StartSectionProps { - onOpenProject: () => void; - isLoading?: boolean; -} - -export function StartSection({ onOpenProject, isLoading }: StartSectionProps) { - return ( -
-

Start

-
- -
-
- ); -} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/StartSection/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/StartSection/index.ts deleted file mode 100644 index 54f382cc712..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/StartSection/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { StartSection } from "./StartSection"; diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/index.ts deleted file mode 100644 index 5def4cd32df..00000000000 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/NewWorkspaceView/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { StartSection } from "./StartSection"; -export { RecentSection } from "./RecentSection"; 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 index 94edb87c598..f7a2c1e7c8c 100644 --- 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 @@ -8,8 +8,8 @@ import { useTabs, useUngroupTab, useUngroupTabs, - useWorkspacesStore, } from "renderer/stores"; +import { trpc } from "renderer/lib/trpc"; import { TabType } from "renderer/stores/tabs/types"; import { TabContextMenu } from "./TabContextMenu"; import type { TabItemProps } from "./types"; @@ -19,9 +19,8 @@ import { useTabRename } from "./useTabRename"; export function TabItem({ tab, childTabs = [] }: TabItemProps) { const [isExpanded, setIsExpanded] = useState(true); - const activeWorkspaceId = useWorkspacesStore( - (state) => state.activeWorkspaceId, - ); + const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); + const activeWorkspaceId = activeWorkspace?.id; const activeTabIds = useActiveTabIds(); const removeTab = useRemoveTab(); const setActiveTab = useSetActiveTab(); 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 index 6bac3397bab..d92fe5dc1cd 100644 --- 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 @@ -2,14 +2,14 @@ import { Button } from "@superset/ui/button"; import { LayoutGroup, motion } from "framer-motion"; import { useMemo } from "react"; import { HiMiniPlus } from "react-icons/hi2"; -import { useAddTab, useTabs, useWorkspacesStore } from "renderer/stores"; +import { useAddTab, useTabs } from "renderer/stores"; +import { trpc } from "renderer/lib/trpc"; import { TabItem } from "./TabItem"; import { UngroupDropZone } from "./UngroupDropZone"; export function TabsView() { - const activeWorkspaceId = useWorkspacesStore( - (state) => state.activeWorkspaceId, - ); + const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); + const activeWorkspaceId = activeWorkspace?.id; const allTabs = useTabs(); const addTab = useAddTab(); 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 c0c32979953..1022d294f5b 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/index.tsx @@ -1,22 +1,7 @@ -import { trpc } from "renderer/lib/trpc"; import { ContentView } from "./ContentView"; -import { NewWorkspaceView } from "./NewWorkspaceView"; import { Sidebar } from "./Sidebar"; export function WorkspaceView() { - const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); - - // If no workspace or workspace has no path, show new workspace view - const isNew = !activeWorkspace || activeWorkspace.path === null; - - if (isNew) { - return ( -
- -
- ); - } - return (
diff --git a/apps/desktop/src/renderer/stores/index.ts b/apps/desktop/src/renderer/stores/index.ts index 298fd3f1375..7af7cf39e13 100644 --- a/apps/desktop/src/renderer/stores/index.ts +++ b/apps/desktop/src/renderer/stores/index.ts @@ -6,4 +6,3 @@ export * from "./sidebar-state"; export * from "./tabs"; // Now exports from tabs/index.ts -export * from "./workspaces"; diff --git a/apps/desktop/src/renderer/stores/workspaces.ts b/apps/desktop/src/renderer/stores/workspaces.ts deleted file mode 100644 index d3e4b9e33cb..00000000000 --- a/apps/desktop/src/renderer/stores/workspaces.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { create } from "zustand"; -import { devtools, persist } from "zustand/middleware"; -import { electronStorage } from "../lib/electron-storage"; - -export interface Workspace { - id: string; - title: string; - isNew?: boolean; -} - -interface WorkspacesState { - workspaces: Workspace[]; - activeWorkspaceId: string | null; - - addWorkspace: () => void; - removeWorkspace: (id: string) => void; - setActiveWorkspace: (id: string) => void; - reorderWorkspaces: (startIndex: number, endIndex: number) => void; - markWorkspaceAsUsed: (id: string) => void; -} - -const createNewWorkspace = (): Workspace => ({ - id: `workspace-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, - title: "New Workspace", - isNew: true, -}); - -export const useWorkspacesStore = create()( - devtools( - persist( - (set) => ({ - workspaces: [], - activeWorkspaceId: null, - - addWorkspace: () => { - const newWorkspace = createNewWorkspace(); - set((state) => ({ - workspaces: [...state.workspaces, newWorkspace], - activeWorkspaceId: newWorkspace.id, - })); - }, - - removeWorkspace: (id) => { - set((state) => { - const workspaces = state.workspaces.filter( - (workspace) => workspace.id !== id, - ); - if (workspaces.length === 0) { - const newWorkspace = createNewWorkspace(); - return { - workspaces: [newWorkspace], - activeWorkspaceId: newWorkspace.id, - }; - } - - if (id === state.activeWorkspaceId) { - const closedIndex = state.workspaces.findIndex( - (workspace) => workspace.id === id, - ); - const nextWorkspace = - workspaces[closedIndex] || workspaces[closedIndex - 1]; - return { workspaces, activeWorkspaceId: nextWorkspace.id }; - } - - return { workspaces }; - }); - }, - - setActiveWorkspace: (id) => { - set({ activeWorkspaceId: id }); - }, - - reorderWorkspaces: (startIndex, endIndex) => { - set((state) => { - const workspaces = [...state.workspaces]; - const [removed] = workspaces.splice(startIndex, 1); - workspaces.splice(endIndex, 0, removed); - return { workspaces }; - }); - }, - - markWorkspaceAsUsed: (id) => { - set((state) => ({ - workspaces: state.workspaces.map((workspace) => - workspace.id === id ? { ...workspace, isNew: false } : workspace, - ), - })); - }, - }), - { - name: "workspaces-storage", - storage: electronStorage, - }, - ), - { name: "WorkspacesStore" }, - ), -); diff --git a/bun.lock b/bun.lock index 3370deb80a4..2451ddbc2ee 100644 --- a/bun.lock +++ b/bun.lock @@ -94,6 +94,7 @@ "dotenv": "^17.2.3", "electron-router-dom": "^2.1.0", "electron-store": "^11.0.2", + "execa": "^9.6.0", "fast-glob": "^3.3.3", "framer-motion": "^12.23.24", "http-proxy": "^1.18.1", @@ -110,6 +111,7 @@ "react-resizable-panels": "^3.0.6", "react-router-dom": "^7.8.2", "react-syntax-highlighter": "^16.1.0", + "simple-git": "^3.30.0", "superjson": "^2.2.5", "tailwind-merge": "^2.6.0", "trpc-electron": "^0.1.2", @@ -770,6 +772,10 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@kwsites/file-exists": ["@kwsites/file-exists@1.1.1", "", { "dependencies": { "debug": "^4.1.1" } }, "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw=="], + + "@kwsites/promise-deferred": ["@kwsites/promise-deferred@1.1.1", "", {}, "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="], + "@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg=="], "@malept/flatpak-bundler": ["@malept/flatpak-bundler@0.4.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", "lodash": "^4.17.15", "tmp-promise": "^3.0.2" } }, "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q=="], @@ -996,6 +1002,8 @@ "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + "@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="], "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="], @@ -1016,6 +1024,8 @@ "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + "@sindresorhus/tsconfig": ["@sindresorhus/tsconfig@3.0.1", "", {}, "sha512-0/gtPNTY3++0J2BZM5nHHULg0BIMw886gqdn8vWN+Av6bgF5ZU2qIcHubAn+Z9KNvJhO8WFE+9kDOU3n6OcKtA=="], "@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="], @@ -2034,7 +2044,7 @@ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "execa": ["execa@9.6.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw=="], "exit-x": ["exit-x@0.2.2", "", {}, "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ=="], @@ -2140,7 +2150,7 @@ "get-stdin": ["get-stdin@9.0.0", "", {}, "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA=="], - "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], @@ -2246,7 +2256,7 @@ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], @@ -2380,7 +2390,7 @@ "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], - "is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="], + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], @@ -2396,7 +2406,7 @@ "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], - "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], @@ -2868,7 +2878,7 @@ "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], - "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], "npm-to-yarn": ["npm-to-yarn@3.0.1", "", {}, "sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A=="], @@ -3320,6 +3330,8 @@ "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "simple-git": ["simple-git@3.30.0", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.4.0" } }, "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg=="], + "simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], @@ -3398,7 +3410,7 @@ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], @@ -3566,6 +3578,8 @@ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], "unique-filename": ["unique-filename@2.0.1", "", { "dependencies": { "unique-slug": "^3.0.0" } }, "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A=="], @@ -3890,6 +3904,8 @@ "cacache/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + "cacheable-request/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + "cacheable-request/lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], "camelcase-keys/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], @@ -4008,7 +4024,13 @@ "eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="], - "execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "execa/figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + + "execa/pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], + + "execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "extract-zip/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], @@ -4058,6 +4080,8 @@ "its-fine/@types/react-reconciler": ["@types/react-reconciler@0.28.9", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="], + "jest-changed-files/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "jest-circus/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-circus/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], @@ -4148,6 +4172,8 @@ "minimist-options/arrify": ["arrify@1.0.1", "", {}, "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA=="], + "minimist-options/is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="], + "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -4174,8 +4200,12 @@ "normalize-package-data/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "open-editor/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "open-editor/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], "ora/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], @@ -4292,8 +4322,6 @@ "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=="], - "unified/is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], - "verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], "vite/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], @@ -4750,6 +4778,8 @@ "eslint/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "execa/pretty-ms/parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "find-cache-dir/pkg-dir/find-up": ["find-up@6.3.0", "", { "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" } }, "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw=="], @@ -4766,6 +4796,16 @@ "ink/react-reconciler/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "jest-changed-files/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "jest-changed-files/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "jest-changed-files/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "jest-changed-files/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "jest-changed-files/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "jest-circus/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-cli/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4820,6 +4860,16 @@ "next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "open-editor/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "open-editor/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "open-editor/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "open-editor/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "open-editor/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "open-editor/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], "open-editor/open/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],