aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/App.css27
-rw-r--r--ui/src/App.tsx237
-rw-r--r--ui/src/assets/icon.svg1
-rw-r--r--ui/src/assets/logo-light.svg1
-rw-r--r--ui/src/assets/react.svg1
-rw-r--r--ui/src/components/Button.tsx20
-rw-r--r--ui/src/components/CodeBlock.tsx39
-rw-r--r--ui/src/components/Drawer.tsx24
-rw-r--r--ui/src/components/HistoryList.tsx33
-rw-r--r--ui/src/components/HistorySearch.tsx54
-rw-r--r--ui/src/components/LoginOrRegister.tsx341
-rw-r--r--ui/src/components/Sidebar/Sidebar.tsx328
-rw-r--r--ui/src/components/Sidebar/index.tsx4
-rw-r--r--ui/src/components/dotfiles/Aliases.tsx180
-rw-r--r--ui/src/components/dotfiles/Vars.tsx194
-rw-r--r--ui/src/components/history/HistoryInspect.tsx40
-rw-r--r--ui/src/components/history/HistoryRow.tsx120
-rw-r--r--ui/src/components/history/Stats.tsx161
-rw-r--r--ui/src/components/home/QuickActions.tsx1
-rw-r--r--ui/src/components/runbooks/List.tsx141
-rw-r--r--ui/src/components/runbooks/editor/Editor.tsx200
-rw-r--r--ui/src/components/runbooks/editor/blocks/Directory/index.tsx89
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/extensions.ts158
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/index.css9
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/index.tsx229
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/terminal.tsx113
-rw-r--r--ui/src/components/runbooks/editor/index.css7
-rw-r--r--ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx28
-rw-r--r--ui/src/components/ui/alert.tsx59
-rw-r--r--ui/src/components/ui/button.tsx56
-rw-r--r--ui/src/components/ui/card.tsx79
-rw-r--r--ui/src/components/ui/chart.tsx363
-rw-r--r--ui/src/components/ui/data-table.tsx80
-rw-r--r--ui/src/components/ui/dialog.tsx120
-rw-r--r--ui/src/components/ui/dropdown-menu.tsx198
-rw-r--r--ui/src/components/ui/table.tsx117
-rw-r--r--ui/src/components/ui/toast.tsx127
-rw-r--r--ui/src/components/ui/toaster.tsx33
-rw-r--r--ui/src/components/ui/use-toast.ts192
-rw-r--r--ui/src/global.d.ts1
-rw-r--r--ui/src/lib/utils.ts48
-rw-r--r--ui/src/main.tsx22
-rw-r--r--ui/src/pages/Dotfiles.tsx109
-rw-r--r--ui/src/pages/History.tsx73
-rw-r--r--ui/src/pages/Home.tsx295
-rw-r--r--ui/src/pages/Runbooks.tsx25
-rw-r--r--ui/src/state/client.ts33
-rw-r--r--ui/src/state/models.ts177
-rw-r--r--ui/src/state/runbooks/runbook.ts124
-rw-r--r--ui/src/state/store.ts289
-rw-r--r--ui/src/styles.css76
-rw-r--r--ui/src/vite-env.d.ts1
52 files changed, 0 insertions, 5477 deletions
diff --git a/ui/src/App.css b/ui/src/App.css
deleted file mode 100644
index 29ca80f1..00000000
--- a/ui/src/App.css
+++ /dev/null
@@ -1,27 +0,0 @@
-html {
- overscroll-behavior: none;
-}
-
-.logo.vite:hover {
- filter: drop-shadow(0 0 2em #747bff);
-}
-
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafb);
-}
-
-.history-header {
- height: 150px;
-}
-
-.history-search {
- height: 64px;
-}
-
-.history-list {
- height: calc(100dvh - 4rem - 2rem);
-}
-
-.history-item {
- height: 90px;
-}
diff --git a/ui/src/App.tsx b/ui/src/App.tsx
deleted file mode 100644
index 361a6fea..00000000
--- a/ui/src/App.tsx
+++ /dev/null
@@ -1,237 +0,0 @@
-import "./App.css";
-import { open } from "@tauri-apps/plugin-shell";
-
-import { useState, ReactElement } from "react";
-import { useStore } from "@/state/store";
-
-import { Toaster } from "@/components/ui/toaster";
-import { KeyRoundIcon } from "lucide-react";
-import { Icon } from "@iconify/react";
-
-import Home from "./pages/Home.tsx";
-import History from "./pages/History.tsx";
-import Dotfiles from "./pages/Dotfiles.tsx";
-import LoginOrRegister from "./components/LoginOrRegister.tsx";
-import Runbooks from "./pages/Runbooks.tsx";
-
-import {
- Avatar,
- User,
- Button,
- ScrollShadow,
- Spacer,
- Dropdown,
- DropdownItem,
- DropdownMenu,
- DropdownSection,
- DropdownTrigger,
- Modal,
- ModalContent,
- useDisclosure,
-} from "@nextui-org/react";
-import Sidebar, { SidebarItem } from "@/components/Sidebar";
-import icon from "@/assets/icon.svg";
-import { logout } from "./state/client.ts";
-
-enum Section {
- Home,
- History,
- Dotfiles,
- Runbooks,
-}
-
-function renderMain(section: Section): ReactElement {
- switch (section) {
- case Section.Home:
- return <Home />;
- case Section.History:
- return <History />;
- case Section.Dotfiles:
- return <Dotfiles />;
- case Section.Runbooks:
- return <Runbooks />;
- }
-}
-
-function App() {
- // routers don't really work in Tauri. It's not a browser!
- // I think hashrouter may work, but I'd rather avoiding thinking of them as
- // pages
- const [section, setSection] = useState(Section.Home);
- const user = useStore((state: any) => state.user);
- const refreshUser = useStore((state: any) => state.refreshUser);
- const { isOpen, onOpen, onOpenChange } = useDisclosure();
-
- const navigation: SidebarItem[] = [
- {
- key: "personal",
- title: "Personal",
- items: [
- {
- key: "home",
- icon: "solar:home-2-linear",
- title: "Home",
- onPress: () => setSection(Section.Home),
- },
- {
- key: "runbooks",
- icon: "solar:notebook-linear",
- title: "Runbooks",
- onPress: () => {
- console.log("runbooks");
- setSection(Section.Runbooks);
- },
- },
- {
- key: "history",
- icon: "solar:history-outline",
- title: "History",
- onPress: () => setSection(Section.History),
- },
- {
- key: "dotfiles",
- icon: "solar:file-smile-linear",
- title: "Dotfiles",
- onPress: () => setSection(Section.Dotfiles),
- },
- ],
- },
- ];
-
- return (
- <div
- className="flex w-screen select-none"
- style={{ maxWidth: "100vw", height: "calc(100dvh - 2rem)" }}
- >
- <div className="flex w-full">
- <div className="relative flex flex-col !border-r-small border-divider transition-width pb-6 pt-4 items-center">
- <div className="flex items-center gap-0 px-3 justify-center">
- <div className="flex h-8 w-8">
- <img src={icon} alt="icon" className="h-8 w-8" />
- </div>
- </div>
-
- <ScrollShadow className="-mr-6 h-full max-h-full py-6 pr-6">
- <Sidebar
- defaultSelectedKey="home"
- isCompact={true}
- items={navigation}
- className="z-50"
- />
- </ScrollShadow>
-
- <Spacer y={2} />
-
- <div className="flex items-center gap-3 px-3">
- <Dropdown showArrow placement="right-start">
- <DropdownTrigger>
- <Button disableRipple isIconOnly radius="full" variant="light">
- <Avatar
- isBordered
- className="flex-none"
- size="sm"
- name={user.username || ""}
- />
- </Button>
- </DropdownTrigger>
- <DropdownMenu aria-label="Custom item styles">
- <DropdownItem
- key="profile"
- isReadOnly
- className="h-14 opacity-100"
- textValue="Signed in as"
- >
- <User
- avatarProps={{
- size: "sm",
- name: user.username || "Anonymous User",
- showFallback: true,
- imgProps: {
- className: "transition-none",
- },
- }}
- classNames={{
- name: "text-default-600",
- description: "text-default-500",
- }}
- description={
- user.bio || (user.username && "No bio") || "Sign up now"
- }
- name={user.username || "Anonymous User"}
- />
- </DropdownItem>
-
- <DropdownItem
- key="settings"
- description="Configure Atuin"
- startContent={
- <Icon icon="solar:settings-linear" width={24} />
- }
- >
- Settings
- </DropdownItem>
-
- <DropdownSection aria-label="Help & Feedback">
- <DropdownItem
- key="help_and_feedback"
- description="Get in touch"
- onPress={() => open("https://forum.atuin.sh")}
- startContent={
- <Icon width={24} icon="solar:question-circle-linear" />
- }
- >
- Help & Feedback
- </DropdownItem>
-
- {(user.username && (
- <DropdownItem
- key="logout"
- startContent={
- <Icon width={24} icon="solar:logout-broken" />
- }
- onClick={() => {
- logout();
- refreshUser();
- }}
- >
- Log Out
- </DropdownItem>
- )) || (
- <DropdownItem
- key="signup"
- description="Sync, backup and share your data"
- className="bg-emerald-100"
- startContent={<KeyRoundIcon size="18px" />}
- onPress={onOpen}
- >
- Log in or Register
- </DropdownItem>
- )}
- </DropdownSection>
- </DropdownMenu>
- </Dropdown>
- </div>
- </div>
-
- {renderMain(section)}
-
- <Toaster />
- <Modal
- isOpen={isOpen}
- onOpenChange={onOpenChange}
- placement="top-center"
- >
- <ModalContent className="p-8">
- {(onClose) => (
- <>
- <LoginOrRegister onClose={onClose} />
- </>
- )}
- </ModalContent>
- </Modal>
- </div>
- </div>
- );
-}
-
-export default App;
diff --git a/ui/src/assets/icon.svg b/ui/src/assets/icon.svg
deleted file mode 100644
index 0e4dd607..00000000
--- a/ui/src/assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" width="344.41" height="309.08" viewBox="0 0 344.41 309.08"><g id="Layer_1-2"><path d="m99.75,291.76c0-5.07,4.12-9.19,9.19-9.19,2.46,0,4.77.96,6.5,2.7,1.4,1.38,2.29,3.16,2.59,5.13.07.41.11.87.11,1.36v10.52s-1.68.27-1.68.27c-2.22.36-4.56.63-6.95.83-2.36.22-4.89.36-7.71.43l-2.05.05v-12.1Zm-2.84,12.05c-.1,0-.19,0-.28,0-2.45-.06-4.4-.15-6.2-.3h-.13c-2.76-.21-5.49-.52-8.1-.94l-1.68-.27v-10.52c0-5.07,4.12-9.19,9.19-9.19,2.46,0,4.77.96,6.5,2.7,1.74,1.72,2.7,4.03,2.7,6.49v12.05h-2Zm22.07-15.26c0-.43.03-.91.1-1.38.68-4.47,4.59-7.82,9.1-7.82,2.44,0,4.74.96,6.49,2.69,1.75,1.76,2.71,4.07,2.71,6.51v5.57s-.54.58-.54.58c-1.71,1.81-4.39,3.42-7.97,4.79-2.19.83-4.7,1.56-7.45,2.17l-2.43.54v-13.65Zm146.15-19.7c0-4.73,3.84-8.58,8.57-8.58,2.29,0,4.44.89,6.06,2.52,1.3,1.3,2.14,2.96,2.41,4.79.06.33.1.77.1,1.28v9.76s-1.68.27-1.68.27c-2.09.33-4.23.59-6.38.76-2.15.2-4.54.33-7.04.39l-2.04.04v-11.22Zm-2.43,11.18c-.08,0-.17,0-.25,0-2.07-.04-3.9-.13-5.68-.27h-.13c-2.55-.2-5.05-.49-7.41-.86l-1.69-.27v-9.37s6.25-1.01,6.25-1.01c3.03-.49,5.97-1.43,8.74-2.8l1,2.01,1-.21c.11.52.16,1.06.16,1.61v11.18h-2Zm20.01-14.12c0-.37.03-.76.08-1.24.65-4.21,4.29-7.33,8.49-7.33,2.28,0,4.43.89,6.06,2.5,1.63,1.63,2.53,3.79,2.53,6.07v5.17s-.55.58-.55.58c-1.6,1.68-4.07,3.17-7.35,4.42-1.95.75-4.25,1.41-6.83,1.99l-2.43.54v-12.69Zm-240.96-5.95c-.09,0-.18,0-.26,0-2.12-.05-3.94-.15-5.66-.28h-.13c-2.46-.18-4.95-.47-7.41-.86l-1.69-.27v-9.77c0-4.31,3.22-7.96,7.49-8.5l1.41-.18,4.2,8.42c1.01,2.01,2.23,3.91,3.61,5.63l.44.55v5.27h-2Z" fill="#fff"/><path d="m14.35,190.88l25.24,50.06c-1.34-.76-2.85-1.16-4.42-1.16-4.96,0-9,4.04-9,9v7.45l-2.45-.57c-1.67-.39-3.27-.84-4.76-1.33-3.39-1.17-9.03-3.68-9.03-7.47v-39.14c.16-6.06,1.7-11.84,4.42-16.84Zm275.98,14.02c-.1-.12-.2-.23-.31-.34l-17.42,42.87c-2.38,5.85-6.64,10.61-11.98,13.64.68.38,1.32.87,1.89,1.44.29.29.55.59.81.93l1.59,2.14,1.61-2.13c1.72-2.28,4.34-3.59,7.18-3.59,2.38,0,4.63.93,6.33,2.61l1.96,1.94,1.23-2.47c1.53-3.05,4.7-5.02,8.06-5.02,1.64,0,3.25.45,4.65,1.28l3.03,1.82v-32.27c-.24-8.66-3.3-16.77-8.63-22.85Zm-163.34,17.02c-6.75-7.69-16.31-11.75-27.65-11.75s-20.91,4.06-27.66,11.75c-5.81,6.62-9.14,15.45-9.39,24.92v42.82c0,4.03,6.09,6.73,9.75,7.99,1.62.53,3.37,1.02,5.19,1.45l2.46.58v-7.92c0-5.53,4.5-10.03,10.03-10.03,2.67,0,5.19,1.04,7.11,2.95.32.32.62.66.91,1.04l1.59,2.14,1.61-2.13c1.92-2.55,4.84-4.01,8.01-4.01,2.66,0,5.16,1.03,7.05,2.91l1.96,1.94,1.24-2.47c1.7-3.4,5.23-5.6,8.99-5.6,1.83,0,3.62.49,5.18,1.43l3.03,1.81v-34.97c-.25-9.42-3.59-18.26-9.39-24.87ZM332.31,60.61c-12.06-21.14-27.47-44.23-52.73-52.68-20.59-7.09-44.02-1.54-59.24,13.76-10.03,9.84-15.89,22.44-19.46,35.27-6.24,23.59-7.07,46.88-7.01,70.75.07,23.56,3.41,44.38,5.07,56.48,1.23,8.87,2.45,16.24,3.36,21.46-4.12,2.78-8.46,6.59-12.06,11.81-9.35,13.58-8.75,29.48-6.64,40.36,1.26,6.49,3.04,11.19,3.59,12.58,5.91,1.03,25.44,4.02,44.35-1.25,1.59,2.58,5.68,4.35,8.37,5.26,1.49.5,3.1.94,4.76,1.33l2.46.58v-7.45c0-1.4.32-2.72.9-3.91l-9.1,1.66c6.45-2.65,12.6-6.46,17.84-11.88,12.54-12.97,15.02-30.01,19.42-60.94,3.06-21.5,3.21-39.68,2.73-52.52,11.25,1.45,23.01-.57,33.51-6.77,6.36-3.67,11.69-8.43,15.91-13.91,12.92-16.83,15.23-40.44,3.98-59.97Z" fill="#26bd65"/><path d="m272.86,215.67c-.92,5.25-1.89,9.97-3.05,14.26l3.05-14.26Z" fill="#26bd65"/><path d="m112.87,102.44c-5.02-.03-9.86-1.12-14.38-3.23l-19.37-9.02c-1.93-.91-2.91-2.84-2.5-4.93.41-2.09,2.05-3.51,4.18-3.61l94.48-4.69c.5-.03,1.02-.04,1.54-.05h.22c1.94,0,3.92.15,5.88.45l10.68,1.68-.25,1.93c-.8,6.24-1.39,12.79-1.75,19.49l-.08,1.6-1.58.26c-1.82.3-3.71.46-5.6.46l-71.47-.35Z" fill="#b3e5fc"/><path d="m280.91,177.2c.86-10.19,1.2-20.5,1-30.64l-.04-2.1,2.1.07c.63.02,1.26.03,1.89.03.89,0,1.77-.02,2.66-.06l1.53-.07.46,1.46c2.85,9.01,2.29,18.71-1.56,27.32-1.06,2.36-2.77,4.38-4.94,5.83l-3.45,2.3.35-4.14Zm-137.27,20.12c2.2,0,4.4-.51,6.37-1.47l44.66-21.73-.19-1.43c-1.58-11.73-3.39-27.44-3.44-44.98,0-4.91,0-10.76.16-16.86v-.46s-.45-1.03-.45-1.03c-1.65-3.67-5.31-6.06-9.32-6.08l-68.83-.34c-4.28,0-8.15,2.7-9.63,6.71l-15.99,43.19c-2.47,6.69.22,14.12,6.38,17.67l42.99,24.85c2.22,1.28,4.74,1.96,7.3,1.96h0Zm-110.02-17.32c2.26,0,4.49-.38,6.62-1.12l32.98-11.48c5.71-1.99,10.19-6.38,12.28-12.04l16.11-43.53c1.88-5.07-.49-10.83-5.39-13.12l-22.31-10.4c-2.09-.98-4.33-1.47-6.64-1.47h-.3c-5.08.09-9.89,2.75-12.88,7.1l-38.98,57.01c-4.92,7.21-3.45,17.12,3.36,22.58l2.55,2.04c3.56,2.85,8.04,4.43,12.61,4.43h0Z" fill="#4fc3f7"/><path d="m162.77,274.65c-4.85,0-9.04-3.62-9.74-8.43l-7.79-53.52c-1.16-7.93,2.91-15.63,10.12-19.15l39.32-19.13,1.07,7.47.38,2.67c.87,6.3,1.84,12.55,2.86,18.57l.2,1.19-.96.73c-4.05,3.09-7.53,6.72-10.34,10.78-9.89,14.37-9.31,31.05-7.08,42.51.89,4.58,2.04,8.32,2.84,10.66l.77,2.24-20.06,3.27c-.57.09-1.06.13-1.53.13h-.06Zm-17.7-.33c-.45,0-.91-.04-1.36-.12l-1.65-.29v-27.24c-.51-20.29-14.11-42.18-42.73-42.18-20.07,0-42,13.21-42.73,42.23v8.28s-3.45-3.63-3.45-3.63c-2.33-2.45-4.07-5.46-5.05-8.7l-13.75-45.29c-2.45-8.07,1.87-16.57,9.84-19.34l31.41-10.93c1.66-.58,3.39-.87,5.16-.87,2.75,0,5.46.73,7.83,2.1l47.65,27.55c4.17,2.41,6.96,6.53,7.67,11.3l8.52,58.55c.32,2.2-.3,4.31-1.73,5.96-1.4,1.64-3.45,2.6-5.61,2.62h-.01ZM11.32,173.97l3.04-2.46,10.23,8.2c3.53,2.82,6.09,6.6,7.4,10.93l12.23,40.27-3.7,1.48-29.2-58.42Zm264.02,43.95c1.26-6.95,2.36-14.63,3.63-23.52l.03-.22c.14-.99.27-2.01.4-3.02v-.09s.12-.5.12-.5c.9-4.25,2.89-8.25,5.75-11.55l8.28-9.59,3.39,2.01-17.75,47.54-3.84-1.06Z" fill="#0288d1"/><path d="m252.88,68.7c-5.28,0-9.56,4.28-9.56,9.56s4.28,9.56,9.56,9.56,9.56-4.28,9.56-9.56-4.28-9.56-9.56-9.56Z" fill="#263238"/><circle cx="317.93" cy="78.26" r="9.56" fill="#263238"/><path d="m310.52,94.56c-1.41,1.55-2.91,2.72-4.44,3.48-1.11.55-2.21.86-3.28.99-.16.02-.32.04-.47.04h-.01c-2.84.2-5.46-.84-7.57-2.15-1.62-.8-3-1.71-4.07-2.5-.4-.29-.88-.49-1.4-.58-.5.12-.96.4-1.29.8-1.53,1.84-4.48,4.75-8.41,5.73-.71.19-1.44.31-2.21.35-.53.02-1.05.01-1.55-.04-4.13-.39-7.31-3.05-8.95-4.71-.94-.96-2.47-.98-3.43-.05-.95.94-.97,2.47-.04,3.43,2.22,2.26,6.68,5.91,12.67,6.2.25.01.5.02.76.02s.52,0,.78-.02c.74-.04,1.45-.12,2.14-.25,4.49-.83,8-3.46,10.31-5.76,4.11,3.18,8.49,4.67,12.69,4.36.66-.05,1.32-.14,1.98-.29,1.19-.25,2.35-.66,3.49-1.22,2.08-1.03,4.06-2.57,5.88-4.56.9-.99.83-2.52-.16-3.42-.99-.9-2.52-.83-3.42.15Z" fill="#263238"/><path d="m336.51,58.2c-13.31-23.33-29.23-46.12-55.36-54.86-22.35-7.7-47.57-1.84-64.2,14.87-9.54,9.36-16.33,21.61-20.75,37.49-1.17,4.42-2.14,8.84-2.97,13.23l-10.17-1.61c-1.8-.28-3.63-.42-5.44-.4-.46,0-.93.01-1.39.04l-103.33,5.13c-10.32.51-19.96,5.9-25.79,14.43L5.8,146.95c-6.81,9.95-7.69,22.82-2.3,33.61l4.02,8.04c-2.64,5.28-4.25,11.58-4.44,18.94v39.31c0,4.21,2.36,10.04,13.68,13.95,7.73,2.53,16.68,3.8,26.69,3.8,3,0,6.1-.11,9.28-.35.61.31,1.24.6,1.87.87v24.54c0,4.61,2.59,10.99,14.97,15.26,8.46,2.77,18.25,4.16,29.2,4.16,3.6,0,7.31-.14,11.16-.44.1-.01.22-.02.33-.04,1.07-.16,2.22-.31,3.42-.47,11.06-1.45,27.76-3.65,30.3-17.6.05-.29.08-.58.08-.87v-5.25l3.96.83c2.3.48,4.66.71,7.02.7,1.7-.02,3.41-.16,5.09-.43l53.15-8.66c4.63-.23,9.46-.77,14.32-1.78,2.08,2.1,5.27,4.13,10.11,5.8,7.74,2.53,16.68,3.81,26.7,3.81,3.28,0,6.68-.13,10.19-.41.1-.01.21-.02.3-.03.99-.15,2.04-.29,3.14-.43,10.1-1.33,25.37-3.33,27.69-16.09.05-.26.07-.53.07-.79v-39.31c-.27-10.59-3.49-18.97-8.44-25.29l10.22-27.36c3.82-10.22,2.37-21.67-3.86-30.62l-.36-.52c3.98-1.28,7.82-2.99,11.48-5.15,6.77-3.91,12.6-9.01,17.33-15.16,8.11-10.56,12.24-23.19,12.24-35.89,0-10.11-2.61-20.25-7.9-29.43Zm-147.48,69.5c.05,17.37,1.84,33.18,3.46,45.24l-43.36,21.1c-3.75,1.83-8.17,1.67-11.79-.42l-42.99-24.85c-5.31-3.06-7.63-9.51-5.51-15.25l15.99-43.19c1.2-3.26,4.32-5.42,7.8-5.4l68.77.34c3.25.02,6.18,1.94,7.51,4.9l.28.62c-.15,5.83-.17,11.48-.16,16.91Zm-110.44-42.06c.18-.92.88-1.93,2.31-2l94.48-4.69c.49-.03.99-.04,1.48-.05,1.92,0,3.86.14,5.76.43l8.75,1.38c-.86,6.67-1.42,13.23-1.76,19.64-1.8.3-3.63.44-5.45.43l-71.28-.35c-4.68-.03-9.3-1.06-13.54-3.04l-19.37-9.02c-1.29-.61-1.56-1.81-1.38-2.73Zm-61.84,66.43l38.98-57.01c2.56-3.74,6.73-6.15,11.26-6.23,2.11-.04,4.16.39,6.07,1.28l22.31,10.4c3.99,1.86,5.89,6.48,4.36,10.61l-16.11,43.53c-1.88,5.09-5.94,9.07-11.06,10.85l-32.98,11.48c-5.9,2.05-12.44.93-17.32-2.98l-2.55-2.04c-6-4.81-7.3-13.54-2.96-19.89Zm7.42,96.7v4.93c-1.58-.37-3.11-.8-4.59-1.28-4.73-1.63-7.65-3.76-7.65-5.57v-39.14c.07-2.67.44-5.38,1.13-8.03l19.23,38.48c-4.67,1.26-8.12,5.54-8.12,10.61Zm17.58,9.19c-.11,0-.21,0-.3,0-1.99-.05-3.8-.14-5.54-.28h-.07c-2.48-.18-4.92-.47-7.24-.84v-8.06c0-3.34,2.5-6.11,5.74-6.52l3.57,7.15c1.08,2.15,2.37,4.16,3.84,5.99v2.57Zm-28.64-84.89l10.23,8.2c3.2,2.56,5.55,6.03,6.74,9.95l12.23,40.27-29.2-58.42Zm83.8,128.74c-.12,0-.23-.01-.33-.01-2.17-.05-4.16-.14-6.06-.3h-.07c-2.72-.2-5.38-.51-7.93-.92v-8.82c0-3.96,3.22-7.19,7.19-7.19,1.92,0,3.73.75,5.09,2.11,1.36,1.35,2.11,3.16,2.11,5.08v10.05Zm19.23-1.23c-2.19.35-4.46.62-6.8.81-2.46.23-5,.36-7.59.42v-10.05c0-3.96,3.22-7.19,7.19-7.19,1.92,0,3.73.75,5.08,2.11,1.11,1.1,1.8,2.48,2.03,4.02.06.34.09.69.09,1.06v8.82Zm1.26-17.37c-2.26-2.24-5.27-3.48-8.46-3.48-3.92,0-7.41,1.89-9.6,4.8-.34-.46-.71-.88-1.12-1.28-2.27-2.26-5.29-3.52-8.51-3.52-6.64,0-12.03,5.4-12.03,12.03v5.39c-1.73-.4-3.41-.87-5.02-1.4-5.17-1.78-8.38-4.11-8.38-6.09v-42.82c.45-16.71,11.6-34.68,35.06-34.68s34.6,17.96,35.04,34.68v31.39c-1.86-1.12-3.99-1.72-6.2-1.72-1.75,0-3.43.38-4.95,1.07-2.51,1.15-4.59,3.14-5.83,5.63Zm17.98,10.12c-1.49,1.58-3.97,3.05-7.23,4.3-2.09.79-4.5,1.49-7.17,2.08v-11.16c0-.36.03-.74.08-1.1.53-3.47,3.59-6.1,7.12-6.1,1.91,0,3.72.76,5.08,2.11,1.36,1.37,2.12,3.18,2.12,5.09v4.78Zm13.8-22.95c-1.04,1.22-2.54,1.92-4.11,1.93-.33,0-.67-.03-1.01-.09v-25.56c-.77-30.35-23.73-44.18-44.73-44.18s-43.97,13.83-44.73,44.18v3.32c-2.09-2.2-3.68-4.89-4.59-7.9l-13.75-45.29c-2.13-7.01,1.66-14.46,8.58-16.87l31.41-10.93c3.75-1.31,7.89-.92,11.33,1.07l47.65,27.55c3.62,2.09,6.08,5.72,6.69,9.86l8.52,58.55c.23,1.6-.21,3.15-1.26,4.36Zm29.67-11.66c.9,4.63,2.05,8.43,2.91,10.93l-17.72,2.89c-.4.06-.81.1-1.21.1-3.9.03-7.26-2.86-7.82-6.72l-7.79-53.52c-1.03-7.05,2.62-13.94,9.02-17.06l36.86-17.93c.16,1.11.31,2.2.46,3.23.21,1.49.41,2.89.59,4.19.86,6.19,1.82,12.45,2.87,18.63-4.23,3.23-7.84,7-10.77,11.23-10.28,14.94-9.7,32.19-7.4,44.03Zm66.28,15.06c-1.59-.37-3.12-.8-4.59-1.29-.05-.01-.1-.03-.15-.05l4.74-.77v2.11Zm17.57-4.93v9.18c-.1,0-.2,0-.3,0-1.98-.04-3.8-.13-5.54-.27h-.06c-2.49-.19-4.92-.47-7.25-.84v-5.96l4.57-.74c3.28-.53,6.41-1.55,9.31-2.98-.28.14-.56.28-.85.41.08.39.12.8.12,1.21Zm17.58,8.06c-2,.32-4.07.57-6.22.74-2.24.21-4.56.33-6.93.38v-9.18c0-3.62,2.94-6.58,6.57-6.58,1.75,0,3.41.69,4.65,1.93,1.01,1.01,1.64,2.27,1.85,3.67.06.31.08.64.08.98v8.06Zm1.15-15.88c-1.78-1.76-4.06-2.84-6.52-3.11-.05.06-.1.12-.15.18,2.15-2.7,3.91-5.74,5.15-9.06l13.45-35.99c2.27,4.62,3.46,9.74,3.6,14.74v28.69c-1.7-1.02-3.65-1.57-5.68-1.57-4.23,0-8.01,2.47-9.85,6.12Zm2.48-114.52c1.58.05,3.14.04,4.7-.03,2.69,8.51,2.17,17.75-1.48,25.9-.94,2.09-2.43,3.78-4.23,4.98.87-10.27,1.21-20.58,1.01-30.85Zm2.87,33.81l8.28-9.59-17.75,47.54c1.28-7.05,2.4-14.92,3.67-23.82.14-1.01.27-2.03.4-3.05l.09-.41c.84-3.95,2.67-7.62,5.31-10.67Zm11.09,85.59v4.37c-1.37,1.44-3.63,2.79-6.61,3.93-1.91.73-4.11,1.36-6.55,1.9v-10.2c0-.33.03-.67.07-1,.49-3.18,3.28-5.57,6.5-5.57,1.76,0,3.41.69,4.65,1.92,1.25,1.25,1.94,2.91,1.94,4.65Zm26.62-148.29c-3.95,5.14-8.82,9.4-14.53,12.7-5.05,2.98-10.52,4.96-16.21,5.9-4.62.77-9.39.85-14.21.23-.06-.01-.12-.01-.18-.01-.02-.01-.04-.01-.07-.02-6.87-.53-13.53-2.18-19.78-4.9-3.9-1.7-7.61-3.8-11.05-6.25-4.46-3.16-8.49-6.9-11.99-11.11-1.7-2.06-4.75-2.34-6.81-.63-2.06,1.7-2.34,4.75-.64,6.81,4.04,4.86,8.69,9.17,13.83,12.82,3.98,2.83,8.28,5.26,12.79,7.23,5.92,2.58,12.14,4.31,18.55,5.2.37,15.84-.56,31.79-2.79,47.5-4.26,29.89-6.61,46.35-18.11,58.26-6.22,6.44-24.61,20.5-62.52,14.7-.72-2.13-1.67-5.33-2.42-9.17-1.94-10.03-2.5-24.53,5.87-36.69,1.44-2.08,3.08-4.01,4.92-5.8.14.56.25,1.12.39,1.68.32,1.29.67,2.59,1.04,3.88.61,2.12,2.55,3.5,4.65,3.5.45,0,.9-.06,1.34-.19,2.57-.74,4.06-3.42,3.32-5.99-.34-1.18-.66-2.37-.96-3.56-.73-2.91-1.35-5.9-1.84-8.86,0-.01,0-.02-.01-.03v-.02c-1.23-7.05-2.35-14.22-3.33-21.29-.18-1.31-.38-2.73-.59-4.23-1.75-12.31-4.38-30.92-4.44-51.61-.05-20.06.62-37.61,3.6-54.59.86-4.98,1.93-9.91,3.23-14.84,3.94-14.14,9.89-24.98,18.23-33.16,14.07-14.14,35.36-19.08,54.27-12.57,23.09,7.72,37.71,28.83,50.07,50.5,9.98,17.33,8.56,38.76-3.62,54.61Z" fill="#263238"/></g></svg> \ No newline at end of file
diff --git a/ui/src/assets/logo-light.svg b/ui/src/assets/logo-light.svg
deleted file mode 100644
index 697df883..00000000
--- a/ui/src/assets/logo-light.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" width="1161.98" height="309.08" viewBox="0 0 1161.98 309.08"><g id="Layer_1-2"><path d="m99.75,291.76c0-5.07,4.12-9.19,9.19-9.19,2.46,0,4.77.96,6.5,2.7,1.4,1.38,2.29,3.16,2.59,5.13.07.41.11.87.11,1.36v10.52s-1.68.27-1.68.27c-2.22.36-4.56.63-6.95.83-2.36.22-4.89.36-7.71.43l-2.05.05v-12.1Zm-2.84,12.05c-.1,0-.19,0-.28,0-2.45-.06-4.4-.15-6.2-.3h-.13c-2.76-.21-5.49-.52-8.1-.94l-1.68-.27v-10.52c0-5.07,4.12-9.19,9.19-9.19,2.46,0,4.77.96,6.5,2.7,1.74,1.72,2.7,4.03,2.7,6.49v12.05h-2Zm22.07-15.26c0-.43.03-.91.1-1.38.68-4.47,4.59-7.82,9.1-7.82,2.44,0,4.74.96,6.49,2.69,1.75,1.76,2.71,4.07,2.71,6.51v5.57s-.54.58-.54.58c-1.71,1.81-4.39,3.42-7.97,4.79-2.19.83-4.7,1.56-7.45,2.17l-2.43.54v-13.65Zm146.15-19.7c0-4.73,3.84-8.58,8.57-8.58,2.29,0,4.44.89,6.06,2.52,1.3,1.3,2.14,2.96,2.41,4.79.06.33.1.77.1,1.28v9.76s-1.68.27-1.68.27c-2.09.33-4.23.59-6.38.76-2.15.2-4.54.33-7.04.39l-2.04.04v-11.22Zm-2.43,11.18c-.08,0-.17,0-.25,0-2.07-.04-3.9-.13-5.68-.27h-.13c-2.55-.2-5.05-.49-7.41-.86l-1.69-.27v-9.37s6.25-1.01,6.25-1.01c3.03-.49,5.97-1.43,8.74-2.8l1,2.01,1-.21c.11.52.16,1.06.16,1.61v11.18h-2Zm20.01-14.12c0-.37.03-.76.08-1.24.65-4.21,4.29-7.33,8.49-7.33,2.28,0,4.43.89,6.06,2.5,1.63,1.63,2.53,3.79,2.53,6.07v5.17s-.55.58-.55.58c-1.6,1.68-4.07,3.17-7.35,4.42-1.95.75-4.25,1.41-6.83,1.99l-2.43.54v-12.69Zm-240.96-5.95c-.09,0-.18,0-.26,0-2.12-.05-3.94-.15-5.66-.28h-.13c-2.46-.18-4.95-.47-7.41-.86l-1.69-.27v-9.77c0-4.31,3.22-7.96,7.49-8.5l1.41-.18,4.2,8.42c1.01,2.01,2.23,3.91,3.61,5.63l.44.55v5.27h-2Z" fill="#fff"/><path d="m14.35,190.88l25.24,50.06c-1.34-.76-2.85-1.16-4.42-1.16-4.96,0-9,4.04-9,9v7.45l-2.45-.57c-1.67-.39-3.27-.84-4.76-1.33-3.39-1.17-9.03-3.68-9.03-7.47v-39.14c.16-6.06,1.7-11.84,4.42-16.84Zm275.98,14.02c-.1-.12-.2-.23-.31-.34l-17.42,42.87c-2.38,5.85-6.64,10.61-11.98,13.64.68.38,1.32.87,1.89,1.44.29.29.55.59.81.93l1.59,2.14,1.61-2.13c1.72-2.28,4.34-3.59,7.18-3.59,2.38,0,4.63.93,6.33,2.61l1.96,1.94,1.23-2.47c1.53-3.05,4.7-5.02,8.06-5.02,1.64,0,3.25.45,4.65,1.28l3.03,1.82v-32.27c-.24-8.66-3.3-16.77-8.63-22.85Zm-163.34,17.02c-6.75-7.69-16.31-11.75-27.65-11.75s-20.91,4.06-27.66,11.75c-5.81,6.62-9.14,15.45-9.39,24.92v42.82c0,4.03,6.09,6.73,9.75,7.99,1.62.53,3.37,1.02,5.19,1.45l2.46.58v-7.92c0-5.53,4.5-10.03,10.03-10.03,2.67,0,5.19,1.04,7.11,2.95.32.32.62.66.91,1.04l1.59,2.14,1.61-2.13c1.92-2.55,4.84-4.01,8.01-4.01,2.66,0,5.16,1.03,7.05,2.91l1.96,1.94,1.24-2.47c1.7-3.4,5.23-5.6,8.99-5.6,1.83,0,3.62.49,5.18,1.43l3.03,1.81v-34.97c-.25-9.42-3.59-18.26-9.39-24.87ZM332.31,60.61c-12.06-21.14-27.47-44.23-52.73-52.68-20.59-7.09-44.02-1.54-59.24,13.76-10.03,9.84-15.89,22.44-19.46,35.27-6.24,23.59-7.07,46.88-7.01,70.75.07,23.56,3.41,44.38,5.07,56.48,1.23,8.87,2.45,16.24,3.36,21.46-4.12,2.78-8.46,6.59-12.06,11.81-9.35,13.58-8.75,29.48-6.64,40.36,1.26,6.49,3.04,11.19,3.59,12.58,5.91,1.03,25.44,4.02,44.35-1.25,1.59,2.58,5.68,4.35,8.37,5.26,1.49.5,3.1.94,4.76,1.33l2.46.58v-7.45c0-1.4.32-2.72.9-3.91l-9.1,1.66c6.45-2.65,12.6-6.46,17.84-11.88,12.54-12.97,15.02-30.01,19.42-60.94,3.06-21.5,3.21-39.68,2.73-52.52,11.25,1.45,23.01-.57,33.51-6.77,6.36-3.67,11.69-8.43,15.91-13.91,12.92-16.83,15.23-40.44,3.98-59.97Z" fill="#26bd65"/><path d="m272.86,215.67c-.92,5.25-1.89,9.97-3.05,14.26l3.05-14.26Z" fill="#26bd65"/><path d="m112.87,102.44c-5.02-.03-9.86-1.12-14.38-3.23l-19.37-9.02c-1.93-.91-2.91-2.84-2.5-4.93.41-2.09,2.05-3.51,4.18-3.61l94.48-4.69c.5-.03,1.02-.04,1.54-.05h.22c1.94,0,3.92.15,5.88.45l10.68,1.68-.25,1.93c-.8,6.24-1.39,12.79-1.75,19.49l-.08,1.6-1.58.26c-1.82.3-3.71.46-5.6.46l-71.47-.35Z" fill="#b3e5fc"/><path d="m280.91,177.2c.86-10.19,1.2-20.5,1-30.64l-.04-2.1,2.1.07c.63.02,1.26.03,1.89.03.89,0,1.77-.02,2.66-.06l1.53-.07.46,1.46c2.85,9.01,2.29,18.71-1.56,27.32-1.06,2.36-2.77,4.38-4.94,5.83l-3.45,2.3.35-4.14Zm-137.27,20.12c2.2,0,4.4-.51,6.37-1.47l44.66-21.73-.19-1.43c-1.58-11.73-3.39-27.44-3.44-44.98,0-4.91,0-10.76.16-16.86v-.46s-.45-1.03-.45-1.03c-1.65-3.67-5.31-6.06-9.32-6.08l-68.83-.34c-4.28,0-8.15,2.7-9.63,6.71l-15.99,43.19c-2.47,6.69.22,14.12,6.38,17.67l42.99,24.85c2.22,1.28,4.74,1.96,7.3,1.96h0Zm-110.02-17.32c2.26,0,4.49-.38,6.62-1.12l32.98-11.48c5.71-1.99,10.19-6.38,12.28-12.04l16.11-43.53c1.88-5.07-.49-10.83-5.39-13.12l-22.31-10.4c-2.09-.98-4.33-1.47-6.64-1.47h-.3c-5.08.09-9.89,2.75-12.88,7.1l-38.98,57.01c-4.92,7.21-3.45,17.12,3.36,22.58l2.55,2.04c3.56,2.85,8.04,4.43,12.61,4.43h0Z" fill="#4fc3f7"/><path d="m162.77,274.65c-4.85,0-9.04-3.62-9.74-8.43l-7.79-53.52c-1.16-7.93,2.91-15.63,10.12-19.15l39.32-19.13,1.07,7.47.38,2.67c.87,6.3,1.84,12.55,2.86,18.57l.2,1.19-.96.73c-4.05,3.09-7.53,6.72-10.34,10.78-9.89,14.37-9.31,31.05-7.08,42.51.89,4.58,2.04,8.32,2.84,10.66l.77,2.24-20.06,3.27c-.57.09-1.06.13-1.53.13h-.06Zm-17.7-.33c-.45,0-.91-.04-1.36-.12l-1.65-.29v-27.24c-.51-20.29-14.11-42.18-42.73-42.18-20.07,0-42,13.21-42.73,42.23v8.28s-3.45-3.63-3.45-3.63c-2.33-2.45-4.07-5.46-5.05-8.7l-13.75-45.29c-2.45-8.07,1.87-16.57,9.84-19.34l31.41-10.93c1.66-.58,3.39-.87,5.16-.87,2.75,0,5.46.73,7.83,2.1l47.65,27.55c4.17,2.41,6.96,6.53,7.67,11.3l8.52,58.55c.32,2.2-.3,4.31-1.73,5.96-1.4,1.64-3.45,2.6-5.61,2.62h-.01ZM11.32,173.97l3.04-2.46,10.23,8.2c3.53,2.82,6.09,6.6,7.4,10.93l12.23,40.27-3.7,1.48-29.2-58.42Zm264.02,43.95c1.26-6.95,2.36-14.63,3.63-23.52l.03-.22c.14-.99.27-2.01.4-3.02v-.09s.12-.5.12-.5c.9-4.25,2.89-8.25,5.75-11.55l8.28-9.59,3.39,2.01-17.75,47.54-3.84-1.06Z" fill="#0288d1"/><path d="m252.88,68.7c-5.28,0-9.56,4.28-9.56,9.56s4.28,9.56,9.56,9.56,9.56-4.28,9.56-9.56-4.28-9.56-9.56-9.56Z" fill="#263238"/><circle cx="317.93" cy="78.26" r="9.56" fill="#263238"/><path d="m310.52,94.56c-1.41,1.55-2.91,2.72-4.44,3.48-1.11.55-2.21.86-3.28.99-.16.02-.32.04-.47.04h-.01c-2.84.2-5.46-.84-7.57-2.15-1.62-.8-3-1.71-4.07-2.5-.4-.29-.88-.49-1.4-.58-.5.12-.96.4-1.29.8-1.53,1.84-4.48,4.75-8.41,5.73-.71.19-1.44.31-2.21.35-.53.02-1.05.01-1.55-.04-4.13-.39-7.31-3.05-8.95-4.71-.94-.96-2.47-.98-3.43-.05-.95.94-.97,2.47-.04,3.43,2.22,2.26,6.68,5.91,12.67,6.2.25.01.5.02.76.02s.52,0,.78-.02c.74-.04,1.45-.12,2.14-.25,4.49-.83,8-3.46,10.31-5.76,4.11,3.18,8.49,4.67,12.69,4.36.66-.05,1.32-.14,1.98-.29,1.19-.25,2.35-.66,3.49-1.22,2.08-1.03,4.06-2.57,5.88-4.56.9-.99.83-2.52-.16-3.42-.99-.9-2.52-.83-3.42.15Z" fill="#263238"/><path d="m336.51,58.2c-13.31-23.33-29.23-46.12-55.36-54.86-22.35-7.7-47.57-1.84-64.2,14.87-9.54,9.36-16.33,21.61-20.75,37.49-1.17,4.42-2.14,8.84-2.97,13.23l-10.17-1.61c-1.8-.28-3.63-.42-5.44-.4-.46,0-.93.01-1.39.04l-103.33,5.13c-10.32.51-19.96,5.9-25.79,14.43L5.8,146.95c-6.81,9.95-7.69,22.82-2.3,33.61l4.02,8.04c-2.64,5.28-4.25,11.58-4.44,18.94v39.31c0,4.21,2.36,10.04,13.68,13.95,7.73,2.53,16.68,3.8,26.69,3.8,3,0,6.1-.11,9.28-.35.61.31,1.24.6,1.87.87v24.54c0,4.61,2.59,10.99,14.97,15.26,8.46,2.77,18.25,4.16,29.2,4.16,3.6,0,7.31-.14,11.16-.44.1-.01.22-.02.33-.04,1.07-.16,2.22-.31,3.42-.47,11.06-1.45,27.76-3.65,30.3-17.6.05-.29.08-.58.08-.87v-5.25l3.96.83c2.3.48,4.66.71,7.02.7,1.7-.02,3.41-.16,5.09-.43l53.15-8.66c4.63-.23,9.46-.77,14.32-1.78,2.08,2.1,5.27,4.13,10.11,5.8,7.74,2.53,16.68,3.81,26.7,3.81,3.28,0,6.68-.13,10.19-.41.1-.01.21-.02.3-.03.99-.15,2.04-.29,3.14-.43,10.1-1.33,25.37-3.33,27.69-16.09.05-.26.07-.53.07-.79v-39.31c-.27-10.59-3.49-18.97-8.44-25.29l10.22-27.36c3.82-10.22,2.37-21.67-3.86-30.62l-.36-.52c3.98-1.28,7.82-2.99,11.48-5.15,6.77-3.91,12.6-9.01,17.33-15.16,8.11-10.56,12.24-23.19,12.24-35.89,0-10.11-2.61-20.25-7.9-29.43Zm-147.48,69.5c.05,17.37,1.84,33.18,3.46,45.24l-43.36,21.1c-3.75,1.83-8.17,1.67-11.79-.42l-42.99-24.85c-5.31-3.06-7.63-9.51-5.51-15.25l15.99-43.19c1.2-3.26,4.32-5.42,7.8-5.4l68.77.34c3.25.02,6.18,1.94,7.51,4.9l.28.62c-.15,5.83-.17,11.48-.16,16.91Zm-110.44-42.06c.18-.92.88-1.93,2.31-2l94.48-4.69c.49-.03.99-.04,1.48-.05,1.92,0,3.86.14,5.76.43l8.75,1.38c-.86,6.67-1.42,13.23-1.76,19.64-1.8.3-3.63.44-5.45.43l-71.28-.35c-4.68-.03-9.3-1.06-13.54-3.04l-19.37-9.02c-1.29-.61-1.56-1.81-1.38-2.73Zm-61.84,66.43l38.98-57.01c2.56-3.74,6.73-6.15,11.26-6.23,2.11-.04,4.16.39,6.07,1.28l22.31,10.4c3.99,1.86,5.89,6.48,4.36,10.61l-16.11,43.53c-1.88,5.09-5.94,9.07-11.06,10.85l-32.98,11.48c-5.9,2.05-12.44.93-17.32-2.98l-2.55-2.04c-6-4.81-7.3-13.54-2.96-19.89Zm7.42,96.7v4.93c-1.58-.37-3.11-.8-4.59-1.28-4.73-1.63-7.65-3.76-7.65-5.57v-39.14c.07-2.67.44-5.38,1.13-8.03l19.23,38.48c-4.67,1.26-8.12,5.54-8.12,10.61Zm17.58,9.19c-.11,0-.21,0-.3,0-1.99-.05-3.8-.14-5.54-.28h-.07c-2.48-.18-4.92-.47-7.24-.84v-8.06c0-3.34,2.5-6.11,5.74-6.52l3.57,7.15c1.08,2.15,2.37,4.16,3.84,5.99v2.57Zm-28.64-84.89l10.23,8.2c3.2,2.56,5.55,6.03,6.74,9.95l12.23,40.27-29.2-58.42Zm83.8,128.74c-.12,0-.23-.01-.33-.01-2.17-.05-4.16-.14-6.06-.3h-.07c-2.72-.2-5.38-.51-7.93-.92v-8.82c0-3.96,3.22-7.19,7.19-7.19,1.92,0,3.73.75,5.09,2.11,1.36,1.35,2.11,3.16,2.11,5.08v10.05Zm19.23-1.23c-2.19.35-4.46.62-6.8.81-2.46.23-5,.36-7.59.42v-10.05c0-3.96,3.22-7.19,7.19-7.19,1.92,0,3.73.75,5.08,2.11,1.11,1.1,1.8,2.48,2.03,4.02.06.34.09.69.09,1.06v8.82Zm1.26-17.37c-2.26-2.24-5.27-3.48-8.46-3.48-3.92,0-7.41,1.89-9.6,4.8-.34-.46-.71-.88-1.12-1.28-2.27-2.26-5.29-3.52-8.51-3.52-6.64,0-12.03,5.4-12.03,12.03v5.39c-1.73-.4-3.41-.87-5.02-1.4-5.17-1.78-8.38-4.11-8.38-6.09v-42.82c.45-16.71,11.6-34.68,35.06-34.68s34.6,17.96,35.04,34.68v31.39c-1.86-1.12-3.99-1.72-6.2-1.72-1.75,0-3.43.38-4.95,1.07-2.51,1.15-4.59,3.14-5.83,5.63Zm17.98,10.12c-1.49,1.58-3.97,3.05-7.23,4.3-2.09.79-4.5,1.49-7.17,2.08v-11.16c0-.36.03-.74.08-1.1.53-3.47,3.59-6.1,7.12-6.1,1.91,0,3.72.76,5.08,2.11,1.36,1.37,2.12,3.18,2.12,5.09v4.78Zm13.8-22.95c-1.04,1.22-2.54,1.92-4.11,1.93-.33,0-.67-.03-1.01-.09v-25.56c-.77-30.35-23.73-44.18-44.73-44.18s-43.97,13.83-44.73,44.18v3.32c-2.09-2.2-3.68-4.89-4.59-7.9l-13.75-45.29c-2.13-7.01,1.66-14.46,8.58-16.87l31.41-10.93c3.75-1.31,7.89-.92,11.33,1.07l47.65,27.55c3.62,2.09,6.08,5.72,6.69,9.86l8.52,58.55c.23,1.6-.21,3.15-1.26,4.36Zm29.67-11.66c.9,4.63,2.05,8.43,2.91,10.93l-17.72,2.89c-.4.06-.81.1-1.21.1-3.9.03-7.26-2.86-7.82-6.72l-7.79-53.52c-1.03-7.05,2.62-13.94,9.02-17.06l36.86-17.93c.16,1.11.31,2.2.46,3.23.21,1.49.41,2.89.59,4.19.86,6.19,1.82,12.45,2.87,18.63-4.23,3.23-7.84,7-10.77,11.23-10.28,14.94-9.7,32.19-7.4,44.03Zm66.28,15.06c-1.59-.37-3.12-.8-4.59-1.29-.05-.01-.1-.03-.15-.05l4.74-.77v2.11Zm17.57-4.93v9.18c-.1,0-.2,0-.3,0-1.98-.04-3.8-.13-5.54-.27h-.06c-2.49-.19-4.92-.47-7.25-.84v-5.96l4.57-.74c3.28-.53,6.41-1.55,9.31-2.98-.28.14-.56.28-.85.41.08.39.12.8.12,1.21Zm17.58,8.06c-2,.32-4.07.57-6.22.74-2.24.21-4.56.33-6.93.38v-9.18c0-3.62,2.94-6.58,6.57-6.58,1.75,0,3.41.69,4.65,1.93,1.01,1.01,1.64,2.27,1.85,3.67.06.31.08.64.08.98v8.06Zm1.15-15.88c-1.78-1.76-4.06-2.84-6.52-3.11-.05.06-.1.12-.15.18,2.15-2.7,3.91-5.74,5.15-9.06l13.45-35.99c2.27,4.62,3.46,9.74,3.6,14.74v28.69c-1.7-1.02-3.65-1.57-5.68-1.57-4.23,0-8.01,2.47-9.85,6.12Zm2.48-114.52c1.58.05,3.14.04,4.7-.03,2.69,8.51,2.17,17.75-1.48,25.9-.94,2.09-2.43,3.78-4.23,4.98.87-10.27,1.21-20.58,1.01-30.85Zm2.87,33.81l8.28-9.59-17.75,47.54c1.28-7.05,2.4-14.92,3.67-23.82.14-1.01.27-2.03.4-3.05l.09-.41c.84-3.95,2.67-7.62,5.31-10.67Zm11.09,85.59v4.37c-1.37,1.44-3.63,2.79-6.61,3.93-1.91.73-4.11,1.36-6.55,1.9v-10.2c0-.33.03-.67.07-1,.49-3.18,3.28-5.57,6.5-5.57,1.76,0,3.41.69,4.65,1.92,1.25,1.25,1.94,2.91,1.94,4.65Zm26.62-148.29c-3.95,5.14-8.82,9.4-14.53,12.7-5.05,2.98-10.52,4.96-16.21,5.9-4.62.77-9.39.85-14.21.23-.06-.01-.12-.01-.18-.01-.02-.01-.04-.01-.07-.02-6.87-.53-13.53-2.18-19.78-4.9-3.9-1.7-7.61-3.8-11.05-6.25-4.46-3.16-8.49-6.9-11.99-11.11-1.7-2.06-4.75-2.34-6.81-.63-2.06,1.7-2.34,4.75-.64,6.81,4.04,4.86,8.69,9.17,13.83,12.82,3.98,2.83,8.28,5.26,12.79,7.23,5.92,2.58,12.14,4.31,18.55,5.2.37,15.84-.56,31.79-2.79,47.5-4.26,29.89-6.61,46.35-18.11,58.26-6.22,6.44-24.61,20.5-62.52,14.7-.72-2.13-1.67-5.33-2.42-9.17-1.94-10.03-2.5-24.53,5.87-36.69,1.44-2.08,3.08-4.01,4.92-5.8.14.56.25,1.12.39,1.68.32,1.29.67,2.59,1.04,3.88.61,2.12,2.55,3.5,4.65,3.5.45,0,.9-.06,1.34-.19,2.57-.74,4.06-3.42,3.32-5.99-.34-1.18-.66-2.37-.96-3.56-.73-2.91-1.35-5.9-1.84-8.86,0-.01,0-.02-.01-.03v-.02c-1.23-7.05-2.35-14.22-3.33-21.29-.18-1.31-.38-2.73-.59-4.23-1.75-12.31-4.38-30.92-4.44-51.61-.05-20.06.62-37.61,3.6-54.59.86-4.98,1.93-9.91,3.23-14.84,3.94-14.14,9.89-24.98,18.23-33.16,14.07-14.14,35.36-19.08,54.27-12.57,23.09,7.72,37.71,28.83,50.07,50.5,9.98,17.33,8.56,38.76-3.62,54.61Z" fill="#263238"/><path d="m425.44,285.08h-44.92c-2.5,0-3.43-1.87-1.87-3.74l161.6-212.76c.94-1.56,2.5-1.87,4.06-1.87h44.92c1.87,0,3.12,1.25,3.12,3.12v212.14c0,1.87-1.25,3.12-3.12,3.12h-44.3c-1.87,0-3.12-1.25-3.12-3.12v-25.89c0-1.56-.94-2.5-2.5-2.5h-84.23c-1.25,0-2.5.62-3.12,1.56l-22.46,28.08c-.94,1.25-2.5,1.87-4.06,1.87Zm69.57-72.06h44.3c1.56,0,2.5-.94,2.5-2.5v-58.34c0-2.18-1.87-2.81-3.12-.94l-44.92,58.65c-1.25,1.87-.62,3.12,1.25,3.12Zm199.03-127.59v41.18c0,1.56.94,2.5,2.5,2.5h21.21c1.87,0,3.12,1.25,3.12,3.12v31.82c0,1.87-1.25,3.12-3.12,3.12h-21.21c-1.56,0-2.5.94-2.5,2.5v112.31c0,1.87-1.25,3.12-3.12,3.12h-44.3c-1.87,0-3.12-1.25-3.12-3.12v-112.31c0-1.56-.94-2.5-2.5-2.5h-23.71c-1.87,0-3.12-1.25-3.12-3.12v-31.82c0-1.87,1.25-3.12,3.12-3.12h23.71c1.56,0,2.5-.94,2.5-2.5v-41.18c0-1.87,1.25-3.12,3.12-3.12h44.3c1.87,0,3.12,1.25,3.12,3.12Zm96.08,46.8l.31,74.87c0,26.21,12.48,33.69,26.52,33.69,22.46,0,30.89-16.53,30.89-35.56v-73c0-1.87,1.25-3.12,3.12-3.12h44.3c1.87,0,3.12,1.25,3.12,3.12v149.74c0,1.87-1.25,3.12-3.12,3.12h-44.3c-1.87,0-3.12-1.25-3.12-3.12v-10.61c0-2.18-1.25-2.5-3.12-1.25-10.3,9.67-23.4,15.29-37.75,15.6-35.25.62-67.07-27.77-67.07-71.13l-.31-82.36c0-1.87,1.25-3.12,3.12-3.12h44.3c1.87,0,3.12.94,3.12,3.12Zm160.66-71.13c15.6,0,27.45,11.85,27.45,26.52s-12.17,26.52-27.45,26.52-27.45-11.85-27.45-26.52,12.17-26.52,27.45-26.52Zm-25.27,220.87v-149.74c0-1.87,1.25-3.12,3.12-3.12h44.3c1.87,0,3.12,1.25,3.12,3.12v149.74c0,1.87-1.25,3.12-3.12,3.12h-44.3c-1.87,0-3.12-1.25-3.12-3.12Zm185.93,0l-.31-74.25c0-26.21-12.48-33.69-26.52-33.69-22.46,0-30.89,16.53-30.89,35.56v72.38c0,1.87-1.25,3.12-3.12,3.12h-44.3c-1.87,0-3.12-1.25-3.12-3.12v-149.74c0-1.87,1.25-3.12,3.12-3.12h44.3c1.87,0,3.12,1.25,3.12,3.12v9.05c0,2.18,1.56,2.81,3.12,1.25,10.3-9.67,23.71-15.91,37.75-15.91,35.25,0,67.07,30.26,67.07,73.62l.31,81.74c0,1.87-1.25,3.12-3.12,3.12h-44.3c-1.87,0-3.12-.94-3.12-3.12Z" fill="#263238"/></g></svg> \ No newline at end of file
diff --git a/ui/src/assets/react.svg b/ui/src/assets/react.svg
deleted file mode 100644
index 6c87de9b..00000000
--- a/ui/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg> \ No newline at end of file
diff --git a/ui/src/components/Button.tsx b/ui/src/components/Button.tsx
deleted file mode 100644
index 5f7e1160..00000000
--- a/ui/src/components/Button.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-export enum ButtonStyle {
- PrimarySm = "bg-emerald-500 hover:bg-emerald-600",
- PrimarySmFill = "bg-emerald-500 hover:bg-emerald-600 w-full text-sm",
-}
-
-interface ButtonProps {
- text: string;
- style: ButtonStyle;
-}
-
-export default function Button(props: ButtonProps) {
- return (
- <button
- type="button"
- className={`rounded ${props.style} px-2 py-1 font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500`}
- >
- {props.text}
- </button>
- );
-}
diff --git a/ui/src/components/CodeBlock.tsx b/ui/src/components/CodeBlock.tsx
deleted file mode 100644
index 4eb54a1c..00000000
--- a/ui/src/components/CodeBlock.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Highlight, themes } from "prism-react-renderer";
-
-// @ts-ignore
-import Prism from "prismjs";
-
-// @ts-ignore
-import "prismjs/components/prism-bash";
-
-export default function CodeBlock({ code, language }: any) {
- return (
- <div className="overflow-auto">
- <Highlight
- theme={themes.github}
- code={code}
- prism={Prism}
- language={language}
- >
- {({ style, tokens, getLineProps, getTokenProps }) => (
- <pre style={style} className="p-4 break-words whitespace-pre-wrap">
- {tokens.map((line, i) => (
- <div key={i} {...getLineProps({ line })} data-vaul-no-drag>
- {i == 0 && (
- <span className="text-gray-500 select-none">$ </span>
- )}
- {line.map((token, key) => (
- <span
- key={key}
- {...getTokenProps({ token })}
- data-vaul-no-drag
- />
- ))}
- </div>
- ))}
- </pre>
- )}
- </Highlight>
- </div>
- );
-}
diff --git a/ui/src/components/Drawer.tsx b/ui/src/components/Drawer.tsx
deleted file mode 100644
index 91753624..00000000
--- a/ui/src/components/Drawer.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Drawer as VDrawer } from "vaul";
-
-export default function Drawer({
- trigger,
- children,
- width,
- open,
- onOpenChange,
-}: any) {
- return (
- <VDrawer.Root direction="right" open={open} onOpenChange={onOpenChange}>
- <VDrawer.Trigger asChild>{trigger}</VDrawer.Trigger>
- <VDrawer.Portal>
- <VDrawer.Overlay className="fixed inset-0 bg-black/40 z-50" />
- <VDrawer.Content
- style={{ width: width || "400px" }}
- className={`bg-white flex flex-col z-50 h-full mt-24 fixed bottom-0 right-0`}
- >
- {children}
- </VDrawer.Content>
- </VDrawer.Portal>
- </VDrawer.Root>
- );
-}
diff --git a/ui/src/components/HistoryList.tsx b/ui/src/components/HistoryList.tsx
deleted file mode 100644
index 948aa5c9..00000000
--- a/ui/src/components/HistoryList.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import HistoryRow from "./history/HistoryRow";
-
-export default function HistoryList(props: any) {
- return (
- <div
- role="list"
- className="divide-y divide-gray-100 bg-white shadow-sm ring-1 ring-gray-900/5 overflow-auto"
- style={{
- height: `${props.height}px`,
- position: "relative",
- }}
- >
- {props.items.map((i: any) => {
- let h = props.history[i.index];
-
- return (
- <div
- style={{
- position: "absolute",
- top: 0,
- left: 0,
- width: "100%",
- height: `${i.size}px`,
- transform: `translateY(${i.start}px)`,
- }}
- >
- <HistoryRow h={h} />
- </div>
- );
- })}
- </div>
- );
-}
diff --git a/ui/src/components/HistorySearch.tsx b/ui/src/components/HistorySearch.tsx
deleted file mode 100644
index 33a3e536..00000000
--- a/ui/src/components/HistorySearch.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { ArrowPathIcon } from "@heroicons/react/24/outline";
-import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
-
-interface HistorySearchProps {
- query: string;
- refresh: () => void;
- setQuery: (query: string) => void;
-}
-
-export default function HistorySearch(props: HistorySearchProps) {
- return (
- <div className="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
- <form
- className="relative flex flex-1"
- onSubmit={(e) => {
- e.preventDefault();
- }}
- >
- <label htmlFor="search-field" className="sr-only">
- Search
- </label>
- <MagnifyingGlassIcon
- className="pointer-events-none absolute inset-y-0 left-0 h-full w-5 text-gray-400"
- aria-hidden="true"
- />
- <input
- id="search-field"
- className="block h-full w-full border-0 py-0 pl-8 pr-0 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm outline-none"
- placeholder="Search..."
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- type="search"
- name="search"
- onChange={(query) => {
- props.setQuery(query.target.value);
- }}
- />
- </form>
- <div className="flex items-center gap-x-4 lg:gap-x-6">
- <button
- type="button"
- className="-m-2.5 p-2.5 text-gray-400 hover:text-gray-500"
- onClick={() => {
- props.refresh();
- }}
- >
- <ArrowPathIcon className="h-6 w-6" aria-hidden="true" />
- </button>
- </div>
- </div>
- );
-}
diff --git a/ui/src/components/LoginOrRegister.tsx b/ui/src/components/LoginOrRegister.tsx
deleted file mode 100644
index 97f8a790..00000000
--- a/ui/src/components/LoginOrRegister.tsx
+++ /dev/null
@@ -1,341 +0,0 @@
-import Logo from "@/assets/logo-light.svg";
-import { useState } from "react";
-
-import { login, register } from "@/state/client";
-import { useStore } from "@/state/store";
-
-interface LoginProps {
- toggleRegister: () => void;
- onClose: () => void;
-}
-
-function Login(props: LoginProps) {
- const refreshUser = useStore((state) => state.refreshUser);
- const [errors, setErrors] = useState<string | null>(null);
-
- const doLogin = async (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
-
- const form = e.currentTarget;
- const username = form.username.value;
- const password = form.password.value;
- const key = form.key.value;
-
- console.log("Logging in...");
- try {
- await login(username, password, key);
- refreshUser();
- props.onClose();
- } catch (e: any) {
- console.error(e);
- setErrors(e);
- }
- };
-
- return (
- <>
- <div className="flex min-h-full flex-1 flex-col justify-center px-6 ">
- <div className="sm:mx-auto sm:w-full sm:max-w-sm">
- <img className="mx-auto h-10 w-auto" src={Logo} alt="Atuin" />
-
- <h2 className="mt-5 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
- Sign in to your account
- </h2>
-
- <p className="text-sm text-center text-gray-600 mt-4 text-wrap">
- Backup and sync your data across devices. All data is end-to-end
- encrypted and stored securely in the cloud.
- </p>
- </div>
-
- <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
- <form
- className="space-y-6"
- action="#"
- method="POST"
- onSubmit={doLogin}
- >
- <div>
- <label
- htmlFor="username"
- className="block text-sm font-medium leading-6 text-gray-900"
- >
- Username
- </label>
- <div className="mt-2">
- <input
- id="username"
- name="username"
- type="username"
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- required
- className="block w-full rounded-md border-0 px-1.5 py-1.5 outline-none text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-emerald-600 sm:text-sm sm:leading-6"
- />
- </div>
- </div>
-
- <div>
- <div className="flex items-center justify-between">
- <label
- htmlFor="password"
- className="block text-sm font-medium leading-6 text-gray-900"
- >
- Password
- </label>
- <div className="text-sm">
- {/* You can't right now. Sorry. Validate emails first.
- <a
- href="#"
- className="font-semibold text-emerald-600 hover:text-emerald-500"
- >
- Forgot password?
- </a>
- */}
- </div>
- </div>
- <div className="mt-2">
- <input
- id="password"
- name="password"
- type="password"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- autoComplete="current-password"
- required
- className="block w-full rounded-md border-0 px-1.5 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset outline-none focus:ring-emerald-600 sm:text-sm sm:leading-6"
- />
- </div>
- </div>
-
- <div>
- <div className="flex items-center justify-between">
- <label
- htmlFor="key"
- className="block text-sm font-medium leading-6 text-gray-900"
- >
- <p>Key</p>
- <p className="text-xs text-gray-500 font-normal">
- Paste the output of "atuin key" from another machine
- </p>
- </label>
- </div>
- <div className="mt-2">
- <input
- id="key"
- name="key"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- autoComplete="off"
- required
- className="block w-full rounded-md border-0 px-1.5 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset outline-none focus:ring-emerald-600 sm:text-sm sm:leading-6"
- />
- </div>
- </div>
-
- <div>
- <button
- type="submit"
- className="flex w-full justify-center rounded-md bg-emerald-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-emerald-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-600"
- >
- Sign in
- </button>
- </div>
- </form>
-
- {errors && (
- <p className="mt-4 text-center text-sm text-red-500">{errors}</p>
- )}
-
- <p className="mt-10 text-center text-sm text-gray-500">
- Not a member?{" "}
- <a
- href="#"
- className="font-semibold leading-6 text-emerald-600 hover:text-emerald-500"
- onClick={(e) => {
- e.preventDefault();
- props.toggleRegister();
- }}
- >
- Register
- </a>
- </p>
- </div>
- </div>
- </>
- );
-}
-
-interface RegisterProps {
- toggleLogin: () => void;
- onClose: () => void;
-}
-
-function Register(props: RegisterProps) {
- const refreshUser = useStore((state) => state.refreshUser);
- const [errors, setErrors] = useState<string | null>(null);
-
- const doRegister = async (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
-
- const form = e.currentTarget;
- const username = form.username.value;
- const email = form.email.value;
- const password = form.password.value;
-
- try {
- await register(username, email, password);
- refreshUser();
- props.onClose();
- } catch (e: any) {
- setErrors(e);
- }
- };
-
- return (
- <>
- <div className="flex min-h-full flex-1 flex-col justify-center px-6 ">
- <div className="sm:mx-auto sm:w-full sm:max-w-sm">
- <img className="mx-auto h-10 w-auto" src={Logo} alt="Atuin" />
-
- <h2 className="mt-5 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
- Register for an account
- </h2>
-
- <p className="text-sm text-center text-gray-600 mt-4 text-wrap">
- Backup and sync your data across devices. All data is end-to-end
- encrypted and stored securely in the cloud.
- </p>
- </div>
-
- <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
- <form
- className="space-y-6"
- action="#"
- method="POST"
- onSubmit={doRegister}
- >
- <div>
- <label
- htmlFor="username"
- className="block text-sm font-medium leading-6 text-gray-900"
- >
- Username
- </label>
- <div className="mt-2">
- <input
- id="username"
- name="username"
- type="username"
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- required
- className="block w-full rounded-md border-0 px-1.5 py-1.5 outline-none text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-emerald-600 sm:text-sm sm:leading-6"
- />
- </div>
- </div>
-
- <div>
- <label
- htmlFor="email"
- className="block text-sm font-medium leading-6 text-gray-900"
- >
- Email
- </label>
- <div className="mt-2">
- <input
- id="email"
- name="email"
- type="email"
- autoComplete="email"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- required
- className="block w-full rounded-md border-0 px-1.5 py-1.5 outline-none text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-emerald-600 sm:text-sm sm:leading-6"
- />
- </div>
- </div>
-
- <div>
- <div className="flex items-center justify-between">
- <label
- htmlFor="password"
- className="block text-sm font-medium leading-6 text-gray-900"
- >
- Password
- </label>
- <div className="text-sm">
- {/* You can't right now. Sorry. Validate emails first.
- <a
- href="#"
- className="font-semibold text-emerald-600 hover:text-emerald-500"
- >
- Forgot password?
- </a>
- */}
- </div>
- </div>
- <div className="mt-2">
- <input
- id="password"
- name="password"
- type="password"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- autoComplete="current-password"
- required
- className="block w-full rounded-md border-0 px-1.5 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset outline-none focus:ring-emerald-600 sm:text-sm sm:leading-6"
- />
- </div>
- </div>
-
- <div>
- <button
- type="submit"
- className="flex w-full justify-center rounded-md bg-emerald-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-emerald-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-600"
- >
- Register
- </button>
- </div>
- </form>
-
- {errors && (
- <p className="mt-4 text-center text-sm text-red-500">{errors}</p>
- )}
-
- <p className="mt-10 text-center text-sm text-gray-500">
- Already have an account?{" "}
- <a
- href="#"
- className="font-semibold leading-6 text-emerald-600 hover:text-emerald-500"
- onClick={(e) => {
- e.preventDefault();
- props.toggleLogin();
- }}
- >
- Login
- </a>
- </p>
- </div>
- </div>
- </>
- );
-}
-
-export default function LoginOrRegister({ onClose }: { onClose: () => void }) {
- let [login, setLogin] = useState<boolean>(false);
-
- if (login) {
- return <Login onClose={onClose} toggleRegister={() => setLogin(false)} />;
- }
-
- return <Register onClose={onClose} toggleLogin={() => setLogin(true)} />;
-}
diff --git a/ui/src/components/Sidebar/Sidebar.tsx b/ui/src/components/Sidebar/Sidebar.tsx
deleted file mode 100644
index 99e2bf82..00000000
--- a/ui/src/components/Sidebar/Sidebar.tsx
+++ /dev/null
@@ -1,328 +0,0 @@
-"use client";
-
-import {
- Accordion,
- AccordionItem,
- type ListboxProps,
- type ListboxSectionProps,
- type Selection,
-} from "@nextui-org/react";
-import React from "react";
-import {
- Listbox,
- Tooltip,
- ListboxItem,
- ListboxSection,
-} from "@nextui-org/react";
-import { Icon } from "@iconify/react";
-
-import { cn } from "@/lib/utils";
-
-export enum SidebarItemType {
- Nest = "nest",
-}
-
-export type SidebarItem = {
- key: string;
- title: string;
- icon?: string;
- href?: string;
- onPress?: () => void;
- type?: SidebarItemType.Nest;
- startContent?: React.ReactNode;
- endContent?: React.ReactNode;
- items?: SidebarItem[];
- className?: string;
-};
-
-export type SidebarProps = Omit<ListboxProps<SidebarItem>, "children"> & {
- items: SidebarItem[];
- isCompact?: boolean;
- hideEndContent?: boolean;
- iconClassName?: string;
- sectionClasses?: ListboxSectionProps["classNames"];
- classNames?: ListboxProps["classNames"];
- defaultSelectedKey: string;
- onSelect?: (key: string) => void;
-};
-
-const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
- (
- {
- items,
- isCompact,
- defaultSelectedKey,
- onSelect,
- hideEndContent,
- sectionClasses: sectionClassesProp = {},
- itemClasses: itemClassesProp = {},
- iconClassName,
- classNames,
- className,
- ...props
- },
- ref,
- ) => {
- const [selected, setSelected] =
- React.useState<React.Key>(defaultSelectedKey);
-
- const sectionClasses = {
- ...sectionClassesProp,
- base: cn(sectionClassesProp?.base, "w-full", {
- "p-0 max-w-[44px]": isCompact,
- }),
- group: cn(sectionClassesProp?.group, {
- "flex flex-col gap-1": isCompact,
- }),
- heading: cn(sectionClassesProp?.heading, {
- hidden: isCompact,
- }),
- };
-
- const itemClasses = {
- ...itemClassesProp,
- base: cn(itemClassesProp?.base, {
- "w-11 h-11 gap-0 p-0": isCompact,
- }),
- };
-
- const renderNestItem = React.useCallback(
- (item: SidebarItem) => {
- const isNestType =
- item.items &&
- item.items?.length > 0 &&
- item?.type === SidebarItemType.Nest;
-
- if (isNestType) {
- // Is a nest type item , so we need to remove the href
- delete item.href;
- }
-
- return (
- <ListboxItem
- {...item}
- key={item.key}
- classNames={{
- base: cn(
- {
- "h-auto p-0": !isCompact && isNestType,
- },
- {
- "inline-block w-11": isCompact && isNestType,
- },
- ),
- }}
- endContent={
- isCompact || isNestType || hideEndContent
- ? null
- : item.endContent ?? null
- }
- startContent={
- isCompact || isNestType ? null : item.icon ? (
- <Icon
- className={cn(
- "text-default-500 group-data-[selected=true]:text-foreground",
- iconClassName,
- )}
- icon={item.icon}
- width={24}
- />
- ) : (
- item.startContent ?? null
- )
- }
- title={isCompact || isNestType ? null : item.title}
- >
- {isCompact ? (
- <Tooltip content={item.title} placement="right">
- <div className="flex w-full items-center justify-center">
- {item.icon ? (
- <Icon
- className={cn(
- "text-default-500 group-data-[selected=true]:text-foreground",
- iconClassName,
- )}
- icon={item.icon}
- width={24}
- />
- ) : (
- item.startContent ?? null
- )}
- </div>
- </Tooltip>
- ) : null}
- {!isCompact && isNestType ? (
- <Accordion className={"p-0"}>
- <AccordionItem
- key={item.key}
- aria-label={item.title}
- classNames={{
- heading: "pr-3",
- trigger: "p-0",
- content: "py-0 pl-4",
- }}
- title={
- item.icon ? (
- <div
- className={"flex h-11 items-center gap-2 px-2 py-1.5"}
- >
- <Icon
- className={cn(
- "text-default-500 group-data-[selected=true]:text-foreground",
- iconClassName,
- )}
- icon={item.icon}
- width={24}
- />
- <span className="text-small font-medium text-default-500 group-data-[selected=true]:text-foreground">
- {item.title}
- </span>
- </div>
- ) : (
- item.startContent ?? null
- )
- }
- >
- {item.items && item.items?.length > 0 ? (
- <Listbox
- className={"mt-0.5"}
- classNames={{
- list: cn("border-l border-default-200 pl-4"),
- }}
- items={item.items}
- variant="flat"
- >
- {item.items.map(renderItem)}
- </Listbox>
- ) : (
- renderItem(item)
- )}
- </AccordionItem>
- </Accordion>
- ) : null}
- </ListboxItem>
- );
- },
- [isCompact, hideEndContent, iconClassName, items],
- );
-
- const renderItem = React.useCallback(
- (item: SidebarItem) => {
- const isNestType =
- item.items &&
- item.items?.length > 0 &&
- item?.type === SidebarItemType.Nest;
-
- if (isNestType) {
- return renderNestItem(item);
- }
-
- return (
- <ListboxItem
- {...item}
- key={item.key}
- endContent={
- isCompact || hideEndContent ? null : item.endContent ?? null
- }
- startContent={
- isCompact ? null : item.icon ? (
- <Icon
- className={cn(
- "text-default-500 group-data-[selected=true]:text-foreground",
- iconClassName,
- )}
- icon={item.icon}
- width={24}
- />
- ) : (
- item.startContent ?? null
- )
- }
- textValue={item.title}
- title={isCompact ? null : item.title}
- >
- {isCompact ? (
- <Tooltip content={item.title} placement="right">
- <div className="flex w-full items-center justify-center">
- {item.icon ? (
- <Icon
- className={cn(
- "text-default-500 group-data-[selected=true]:text-foreground",
- iconClassName,
- )}
- icon={item.icon}
- width={24}
- />
- ) : (
- item.startContent ?? null
- )}
- </div>
- </Tooltip>
- ) : null}
- </ListboxItem>
- );
- },
- [isCompact, hideEndContent, iconClassName, itemClasses?.base],
- );
-
- return (
- <Listbox
- key={isCompact ? "compact" : "default"}
- ref={ref}
- hideSelectedIcon
- as="nav"
- className={cn("list-none", className)}
- classNames={{
- ...classNames,
- list: cn("items-center", classNames?.list),
- }}
- color="default"
- itemClasses={{
- ...itemClasses,
- base: cn(
- "px-3 min-h-11 rounded-large h-[44px] data-[selected=true]:bg-default-100",
- itemClasses?.base,
- ),
- title: cn(
- "text-small font-medium text-default-500 group-data-[selected=true]:text-foreground",
- itemClasses?.title,
- ),
- }}
- items={items}
- selectedKeys={[selected] as unknown as Selection}
- selectionMode="single"
- variant="flat"
- onSelectionChange={(keys) => {
- const key = Array.from(keys)[0];
-
- setSelected(key as React.Key);
- onSelect?.(key as string);
- }}
- {...props}
- >
- {(item) => {
- return item.items &&
- item.items?.length > 0 &&
- item?.type === SidebarItemType.Nest ? (
- renderNestItem(item)
- ) : item.items && item.items?.length > 0 ? (
- <ListboxSection
- key={item.key}
- classNames={sectionClasses}
- showDivider={isCompact}
- title={item.title}
- >
- {item.items.map(renderItem)}
- </ListboxSection>
- ) : (
- renderItem(item)
- );
- }}
- </Listbox>
- );
- },
-);
-
-Sidebar.displayName = "Sidebar";
-
-export default Sidebar;
diff --git a/ui/src/components/Sidebar/index.tsx b/ui/src/components/Sidebar/index.tsx
deleted file mode 100644
index 10020952..00000000
--- a/ui/src/components/Sidebar/index.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-import Sidebar, { SidebarItem } from "./Sidebar";
-
-export type { SidebarItem };
-export default Sidebar;
diff --git a/ui/src/components/dotfiles/Aliases.tsx b/ui/src/components/dotfiles/Aliases.tsx
deleted file mode 100644
index 61fd001c..00000000
--- a/ui/src/components/dotfiles/Aliases.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-import { useEffect, useState } from "react";
-
-import DataTable from "@/components/ui/data-table";
-import { Button } from "@/components/ui/button";
-import { MoreHorizontal } from "lucide-react";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-
-import { ColumnDef } from "@tanstack/react-table";
-
-import { invoke } from "@tauri-apps/api/core";
-import Drawer from "@/components/Drawer";
-
-import { Alias } from "@/state/models";
-import { useStore } from "@/state/store";
-
-function deleteAlias(name: string, refreshAliases: () => void) {
- invoke("delete_alias", { name: name })
- .then(() => {
- refreshAliases();
- })
- .catch(() => {
- console.error("Failed to delete alias");
- });
-}
-
-function AddAlias({ onAdd: onAdd }: { onAdd?: () => void }) {
- let [name, setName] = useState("");
- let [value, setValue] = useState("");
-
- // simple form to add aliases
- return (
- <div className="p-4">
- <h2 className="text-xl font-semibold leading-6 text-gray-900">
- Add alias
- </h2>
- <p className="mt-2">Add a new alias to your shell</p>
-
- <form
- className="mt-4"
- onSubmit={(e) => {
- e.preventDefault();
-
- invoke("set_alias", { name: name, value: value })
- .then(() => {
- console.log("Added alias");
-
- if (onAdd) onAdd();
- })
- .catch(() => {
- console.error("Failed to add alias");
- });
- }}
- >
- <input
- className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-md focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
- type="text"
- value={name}
- onChange={(e) => setName(e.target.value)}
- placeholder="Alias name"
- />
-
- <input
- className="mt-4 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-md focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- type="text"
- value={value}
- onChange={(e) => setValue(e.target.value)}
- placeholder="Alias value"
- />
-
- <input
- type="submit"
- className="block mt-4 rounded-md bg-green-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
- value="Add alias"
- />
- </form>
- </div>
- );
-}
-
-export default function Aliases() {
- const aliases = useStore((state) => state.aliases);
- const refreshAliases = useStore((state) => state.refreshAliases);
-
- let [aliasDrawerOpen, setAliasDrawerOpen] = useState(false);
-
- const columns: ColumnDef<Alias>[] = [
- {
- accessorKey: "name",
- header: "Name",
- },
- {
- accessorKey: "value",
- header: "Value",
- },
- {
- id: "actions",
- cell: ({ row }: any) => {
- const alias = row.original;
-
- return (
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button variant="ghost" className="h-8 w-8 p-0 float-right">
- <span className="sr-only">Open menu</span>
- <MoreHorizontal className="h-4 w-4 text-right" />
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end">
- <DropdownMenuLabel>Actions</DropdownMenuLabel>
- <DropdownMenuItem
- onClick={() => deleteAlias(alias.name, refreshAliases)}
- >
- Delete
- </DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- );
- },
- },
- ];
-
- useEffect(() => {
- refreshAliases();
- }, []);
-
- return (
- <div className="pt-10">
- <div className="sm:flex sm:items-center">
- <div className="sm:flex-auto">
- <h1 className="text-base font-semibold leading-6 text-gray-900">
- Aliases
- </h1>
- <p className="mt-2 text-sm text-gray-700">
- Aliases allow you to condense long commands into short,
- easy-to-remember commands.
- </p>
- </div>
- <div className="mt-4 sm:ml-16 sm:mt-0 flex-row">
- <Drawer
- open={aliasDrawerOpen}
- onOpenChange={setAliasDrawerOpen}
- width="30%"
- trigger={
- <button
- type="button"
- className="block rounded-md bg-green-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
- >
- Add
- </button>
- }
- >
- <AddAlias
- onAdd={() => {
- refreshAliases();
- setAliasDrawerOpen(false);
- }}
- />
- </Drawer>
- </div>
- </div>
- <div className="mt-8 flow-root">
- <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
- <div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
- <DataTable columns={columns} data={aliases} />
- </div>
- </div>
- </div>
- </div>
- );
-}
diff --git a/ui/src/components/dotfiles/Vars.tsx b/ui/src/components/dotfiles/Vars.tsx
deleted file mode 100644
index b2379aa7..00000000
--- a/ui/src/components/dotfiles/Vars.tsx
+++ /dev/null
@@ -1,194 +0,0 @@
-import { useEffect, useState } from "react";
-
-import DataTable from "@/components/ui/data-table";
-import { Button } from "@/components/ui/button";
-import { MoreHorizontal } from "lucide-react";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-
-import { ColumnDef } from "@tanstack/react-table";
-
-import { invoke } from "@tauri-apps/api/core";
-import Drawer from "@/components/Drawer";
-
-import { Var } from "@/state/models";
-import { useStore } from "@/state/store";
-
-function deleteVar(name: string, refreshVars: () => void) {
- invoke("delete_var", { name: name })
- .then(() => {
- refreshVars();
- })
- .catch(() => {
- console.error("Failed to delete var");
- });
-}
-
-function AddVar({ onAdd: onAdd }: { onAdd?: () => void }) {
- let [name, setName] = useState("");
- let [value, setValue] = useState("");
- let [exp, setExport] = useState<boolean>(false);
-
- // simple form to add vars
- return (
- <div className="p-4">
- <h2 className="text-xl font-semibold leading-6 text-gray-900">Add var</h2>
- <p className="mt-2">Add a new var to your shell</p>
-
- <form
- className="mt-4"
- onSubmit={(e) => {
- e.preventDefault();
-
- invoke("set_var", { name: name, value: value, export: exp })
- .then(() => {
- console.log("Added var");
-
- if (onAdd) onAdd();
- })
- .catch(() => {
- console.error("Failed to add var");
- });
- }}
- >
- <input
- className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-md focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
- type="text"
- value={name}
- onChange={(e) => setName(e.target.value)}
- placeholder="Var name"
- />
-
- <input
- className="mt-4 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-md focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- type="text"
- value={value}
- onChange={(e) => setValue(e.target.value)}
- placeholder="Var value"
- />
-
- <div>
- <label>
- <input
- className="mt-4 bg-gray-50 mr-2 inline"
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- type="checkbox"
- value={exp.toString()}
- onChange={(e) => setExport(e.target.checked)}
- />
- Export the var and make it visible to subprocesses
- </label>
- </div>
-
- <input
- type="submit"
- className="block mt-4 rounded-md bg-green-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
- value="Add var"
- />
- </form>
- </div>
- );
-}
-
-export default function Vars() {
- const vars = useStore((state) => state.vars);
- const refreshVars = useStore((state) => state.refreshVars);
-
- let [varDrawerOpen, setVarDrawerOpen] = useState(false);
-
- const columns: ColumnDef<Var>[] = [
- {
- accessorKey: "name",
- header: "Name",
- },
- {
- accessorKey: "value",
- header: "Value",
- },
- {
- id: "actions",
- cell: ({ row }: any) => {
- const shell_var = row.original;
-
- return (
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button variant="ghost" className="h-8 w-8 p-0 float-right">
- <span className="sr-only">Open menu</span>
- <MoreHorizontal className="h-4 w-4 text-right" />
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end">
- <DropdownMenuLabel>Actions</DropdownMenuLabel>
- <DropdownMenuItem
- onClick={() => deleteVar(shell_var.name, refreshVars)}
- >
- Delete
- </DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- );
- },
- },
- ];
-
- useEffect(() => {
- refreshVars();
- }, []);
-
- return (
- <div className="pt-10">
- <div className="sm:flex sm:items-center">
- <div className="sm:flex-auto">
- <h1 className="text-base font-semibold leading-6 text-gray-900">
- Vars
- </h1>
- <p className="mt-2 text-sm text-gray-700">
- Configure environment variables here
- </p>
- </div>
- <div className="mt-4 sm:ml-16 sm:mt-0 flex-row">
- <Drawer
- open={varDrawerOpen}
- onOpenChange={setVarDrawerOpen}
- width="30%"
- trigger={
- <button
- type="button"
- className="block rounded-md bg-green-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
- >
- Add
- </button>
- }
- >
- <AddVar
- onAdd={() => {
- refreshVars();
- setVarDrawerOpen(false);
- }}
- />
- </Drawer>
- </div>
- </div>
- <div className="mt-8 flow-root">
- <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
- <div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
- <DataTable columns={columns} data={vars} />
- </div>
- </div>
- </div>
- </div>
- );
-}
diff --git a/ui/src/components/history/HistoryInspect.tsx b/ui/src/components/history/HistoryInspect.tsx
deleted file mode 100644
index 6c46f2db..00000000
--- a/ui/src/components/history/HistoryInspect.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useState, useEffect } from "react";
-
-import PacmanLoader from "react-spinners/PacmanLoader";
-
-import CodeBlock from "@/components/CodeBlock";
-import HistoryRow from "@/components/history/HistoryRow";
-import { ShellHistory, inspectCommandHistory } from "@/state/models";
-
-function renderLoading() {
- return (
- <div className="flex items-center justify-center h-full">
- <PacmanLoader color="#26bd65" />
- </div>
- );
-}
-
-export default function HistoryInspect({ history }: any) {
- let [other, setOther] = useState<ShellHistory[]>([]);
-
- useEffect(() => {
- (async () => {
- let inspect = await inspectCommandHistory(history);
- setOther(inspect.other);
- })();
- }, []);
-
- if (other.length == 0) return renderLoading();
-
- return (
- <div className="overflow-y-auto">
- <CodeBlock code={history.command} language="bash" />
-
- <div>
- {other.map((i: any) => {
- return <HistoryRow h={i} />;
- })}
- </div>
- </div>
- );
-}
diff --git a/ui/src/components/history/HistoryRow.tsx b/ui/src/components/history/HistoryRow.tsx
deleted file mode 100644
index 4d893e61..00000000
--- a/ui/src/components/history/HistoryRow.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-// @ts-ignore
-import { DateTime } from "luxon";
-import { ChevronRightIcon } from "@heroicons/react/20/solid";
-import { Highlight, themes } from "prism-react-renderer";
-
-// @ts-ignore
-import Prism from "prismjs";
-
-// @ts-ignore
-import "prismjs/components/prism-bash";
-
-import Drawer from "../Drawer";
-import HistoryInspect from "./HistoryInspect";
-import { cn } from "@/lib/utils";
-
-function msToTime(ms: number) {
- let milliseconds = parseInt(ms.toFixed(1));
- let seconds = parseInt((ms / 1000).toFixed(1));
- let minutes = parseInt((ms / (1000 * 60)).toFixed(1));
- let hours = parseInt((ms / (1000 * 60 * 60)).toFixed(1));
- let days = parseInt((ms / (1000 * 60 * 60 * 24)).toFixed(1));
-
- if (milliseconds < 1000) return milliseconds + "ms";
- else if (seconds < 60) return seconds + "s";
- else if (minutes < 60) return minutes + "m";
- else if (hours < 24) return hours + "hr";
- else return days + " Days";
-}
-
-export default function HistoryRow({ h, compact }: any) {
- return (
- <li
- key={h.id}
- className={cn(
- "relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6",
- { "py-5": !compact },
- { "py-1": compact },
- )}
- >
- <div className="flex min-w-0 gap-x-4">
- {!compact && (
- <div className="flex flex-col justify-center">
- <p className="flex text-xs text-gray-500 justify-center">
- {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
- DateTime.TIME_WITH_SECONDS,
- )}
- </p>
- <p className="flex text-xs mt-1 text-gray-400 justify-center">
- {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
- DateTime.DATE_SHORT,
- )}
- </p>
- </div>
- )}
- <div className="min-w-0 flex-col justify-center truncate">
- <Highlight
- theme={themes.github}
- code={h.command}
- language="bash"
- prism={Prism}
- >
- {({ style, tokens, getLineProps, getTokenProps }) => (
- <pre style={style} className="!bg-inherit text-sm">
- {tokens &&
- tokens.map((line, i) => {
- if (i != 0) return;
- return (
- <div key={i} {...getLineProps({ line })}>
- {line.map((token, key) => (
- <span key={key} {...getTokenProps({ token })} />
- ))}
- </div>
- );
- })}
- </pre>
- )}
- </Highlight>
- <p className="mt-1 flex text-xs leading-5 text-gray-500">
- <span className="relative truncate ">{h.user}</span>
-
- <span>&nbsp;on&nbsp;</span>
-
- <span className="relative truncate ">{h.host}</span>
-
- <span>&nbsp;in&nbsp;</span>
-
- <span className="relative truncate ">{h.cwd}</span>
- </p>
- </div>
- </div>
- <div className="flex shrink-0 items-center gap-x-4">
- <div className="hidden sm:flex sm:flex-col sm:items-end">
- <p className="text-sm leading-6 text-gray-900">{h.exit}</p>
- {h.duration ? (
- <p className="mt-1 text-xs leading-5 text-gray-500">
- <time dateTime={h.duration}>
- {msToTime(h.duration / 1000000)}
- </time>
- </p>
- ) : (
- <div />
- )}
- </div>
- <Drawer
- width="60%"
- trigger={
- <button type="button">
- <ChevronRightIcon
- className="h-5 w-5 flex-none text-gray-400"
- aria-hidden="true"
- />
- </button>
- }
- >
- <HistoryInspect history={h} />
- </Drawer>
- </div>
- </li>
- );
-}
diff --git a/ui/src/components/history/Stats.tsx b/ui/src/components/history/Stats.tsx
deleted file mode 100644
index f399eaf0..00000000
--- a/ui/src/components/history/Stats.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-import { useState, useEffect } from "react";
-import { invoke } from "@tauri-apps/api/core";
-import PacmanLoader from "react-spinners/PacmanLoader";
-
-import {
- BarChart,
- Bar,
- XAxis,
- YAxis,
- Tooltip,
- ResponsiveContainer,
-} from "recharts";
-
-function renderLoading() {
- return (
- <div className="flex flex-col items-center justify-center h-full ">
- <div>
- <PacmanLoader color="#26bd65" />
- </div>
- <div className="block mt-4">
- <p>Crunching the latest numbers...</p>
- </div>
- </div>
- );
-}
-
-function TopTable({ stats }: any) {
- return (
- <div className="px-4 sm:px-6 lg:px-8">
- <div className="flex items-center">
- <div className="flex-auto">
- <h1 className="text-base font-semibold">Top commands</h1>
- </div>
- </div>
- <div className="mt-4 flow-root">
- <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
- <div className="inline-block min-w-full py-2 align-middle">
- <table className="min-w-full divide-y divide-gray-300">
- <thead>
- <tr>
- <th
- scope="col"
- className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8"
- >
- Command
- </th>
- <th
- scope="col"
- className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
- >
- Count
- </th>
- </tr>
- </thead>
- <tbody className="divide-y divide-gray-200 bg-white">
- {stats.map((stat: any) => (
- <tr>
- <td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8">
- {stat[0][0]}
- </td>
- <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
- {stat[1]}
- </td>
- </tr>
- ))}
- </tbody>
- </table>
- </div>
- </div>
- </div>
- </div>
- );
-}
-
-export default function Stats() {
- const [stats, setStats]: any = useState([]);
- const [top, setTop]: any = useState([]);
- const [chart, setChart]: any = useState([]);
-
- useEffect(() => {
- if (stats.length != 0) return;
-
- invoke("global_stats")
- .then((s: any) => {
- console.log(s.daily);
-
- setStats([
- {
- name: "Total history",
- stat: s.total_history.toLocaleString(),
- },
- {
- name: "Unique history",
- stat: s.stats.unique_commands.toLocaleString(),
- },
- {
- name: "Last 1d",
- stat: s.last_1d.toLocaleString(),
- },
- {
- name: "Last 7d",
- stat: s.last_7d.toLocaleString(),
- },
- {
- name: "Last 30d",
- stat: s.last_30d.toLocaleString(),
- },
- ]);
-
- setChart(s.daily);
-
- setTop(s.stats);
- })
- .catch((e) => {
- console.log(e);
- });
- }, []);
-
- if (stats.length == 0) {
- return renderLoading();
- }
-
- return (
- <div className="flex flex-col overflow-y-scroll">
- <div className="flexfull">
- <dl className="grid grid-cols-1 sm:grid-cols-5 w-full">
- {stats.map((item: any) => (
- <div
- key={item.name}
- className="overflow-hidden bg-white px-4 py-5 shadow sm:p-6"
- >
- <dt className="truncate text-sm font-medium text-gray-500">
- {item.name}
- </dt>
- <dd className="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
- {item.stat}
- </dd>
- </div>
- ))}
- </dl>
- </div>
-
- <div className="flex flex-col h-54 py-4 pl-5">
- <div className="flex flex-col h-48 pt-5 pr-5">
- <ResponsiveContainer width="100%" height="100%">
- <BarChart width={500} height={300} data={chart}>
- <XAxis dataKey="name" hide={true} />
- <YAxis />
- <Tooltip />
- <Bar dataKey="value" fill="#26bd65" />
- </BarChart>
- </ResponsiveContainer>
- </div>
- </div>
-
- <div>
- <TopTable stats={top.top} />
- </div>
- </div>
- );
-}
diff --git a/ui/src/components/home/QuickActions.tsx b/ui/src/components/home/QuickActions.tsx
deleted file mode 100644
index a22e4493..00000000
--- a/ui/src/components/home/QuickActions.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export default function QuickActions() {}
diff --git a/ui/src/components/runbooks/List.tsx b/ui/src/components/runbooks/List.tsx
deleted file mode 100644
index 42da3885..00000000
--- a/ui/src/components/runbooks/List.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import { useEffect } from "react";
-import {
- Button,
- ButtonGroup,
- Tooltip,
- Listbox,
- ListboxItem,
- Dropdown,
- DropdownTrigger,
- DropdownMenu,
- DropdownItem,
- Badge,
-} from "@nextui-org/react";
-
-import { EllipsisVerticalIcon } from "lucide-react";
-
-import { DateTime } from "luxon";
-
-import { NotebookPenIcon } from "lucide-react";
-import Runbook from "@/state/runbooks/runbook";
-import { AtuinState, useStore } from "@/state/store";
-
-const NoteSidebar = () => {
- const runbooks = useStore((state: AtuinState) => state.runbooks);
- const refreshRunbooks = useStore(
- (state: AtuinState) => state.refreshRunbooks,
- );
-
- const currentRunbook = useStore((state: AtuinState) => state.currentRunbook);
- const setCurrentRunbook = useStore(
- (state: AtuinState) => state.setCurrentRunbook,
- );
- const runbookInfo = useStore((state: AtuinState) => state.runbookInfo);
-
- useEffect(() => {
- refreshRunbooks();
- }, []);
-
- return (
- <div className="w-48 flex flex-col border-r-1">
- <div className="overflow-y-auto flex-grow">
- <Listbox
- hideSelectedIcon
- items={runbooks.map((runbook: any): any => {
- return [runbook, runbookInfo[runbook.id]];
- })}
- variant="flat"
- aria-label="Runbook list"
- selectionMode="single"
- selectedKeys={currentRunbook ? [currentRunbook] : []}
- itemClasses={{ base: "data-[selected=true]:bg-gray-200" }}
- topContent={
- <ButtonGroup className="z-20">
- <Tooltip showArrow content="New Runbook" closeDelay={50}>
- <Button
- isIconOnly
- aria-label="New note"
- variant="light"
- size="sm"
- onPress={async () => {
- // otherwise the cursor is weirdly positioned in the new document
- window.getSelection()?.removeAllRanges();
-
- let runbook = await Runbook.create();
- setCurrentRunbook(runbook.id);
- refreshRunbooks();
- }}
- >
- <NotebookPenIcon className="p-[0.15rem]" />
- </Button>
- </Tooltip>
- </ButtonGroup>
- }
- >
- {([runbook, info]: [Runbook, { ptys: number }]) => (
- <ListboxItem
- key={runbook.id}
- onPress={() => {
- setCurrentRunbook(runbook.id);
- }}
- textValue={runbook.name || "Untitled"}
- endContent={
- <Dropdown>
- <Badge
- content={info?.ptys}
- color="primary"
- style={
- info && info?.ptys > 0
- ? {}
- : {
- display: "none",
- }
- }
- >
- <DropdownTrigger className="bg-transparent">
- <Button isIconOnly>
- <EllipsisVerticalIcon
- size="16px"
- className="bg-transparent"
- />
- </Button>
- </DropdownTrigger>
- </Badge>
- <DropdownMenu aria-label="Dynamic Actions">
- <DropdownItem
- key={"delete"}
- color="danger"
- className="text-danger"
- onPress={async () => {
- await Runbook.delete(runbook.id);
-
- if (runbook.id == currentRunbook) setCurrentRunbook("");
-
- refreshRunbooks();
- }}
- >
- Delete
- </DropdownItem>
- </DropdownMenu>
- </Dropdown>
- }
- >
- <div className="flex flex-col">
- <div className="text-md">{runbook.name || "Untitled"}</div>
- <div className="text-xs text-gray-500">
- <em>
- {DateTime.fromJSDate(runbook.updated).toLocaleString(
- DateTime.DATETIME_SHORT,
- )}
- </em>
- </div>
- </div>
- </ListboxItem>
- )}
- </Listbox>
- </div>
- </div>
- );
-};
-
-export default NoteSidebar;
diff --git a/ui/src/components/runbooks/editor/Editor.tsx b/ui/src/components/runbooks/editor/Editor.tsx
deleted file mode 100644
index 6b0522f5..00000000
--- a/ui/src/components/runbooks/editor/Editor.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-import { useEffect, useMemo, useState } from "react";
-
-import "./index.css";
-
-import { Spinner } from "@nextui-org/react";
-
-// Errors, but it all works fine and is there. Maybe missing ts defs?
-// I'll figure it out later
-import {
- // @ts-ignore
- BlockNoteSchema,
- // @ts-ignore
- BlockNoteEditor,
- // @ts-ignore
- defaultBlockSpecs,
- // @ts-ignore
- filterSuggestionItems,
- // @ts-ignore
- insertOrUpdateBlock,
-} from "@blocknote/core";
-
-import {
- //@ts-ignore
- SuggestionMenuController,
- // @ts-ignore
- AddBlockButton,
- // @ts-ignore
- getDefaultReactSlashMenuItems,
- // @ts-ignore
- SideMenu,
- // @ts-ignore
- SideMenuController,
-} from "@blocknote/react";
-import { BlockNoteView } from "@blocknote/mantine";
-
-import "@blocknote/core/fonts/inter.css";
-import "@blocknote/mantine/style.css";
-
-import { CodeIcon, FolderOpenIcon } from "lucide-react";
-import { useDebounceCallback } from "usehooks-ts";
-
-import Run from "@/components/runbooks/editor/blocks/Run";
-import Directory from "@/components/runbooks/editor/blocks/Directory";
-
-import { DeleteBlock } from "@/components/runbooks/editor/ui/DeleteBlockButton";
-import { AtuinState, useStore } from "@/state/store";
-import Runbook from "@/state/runbooks/runbook";
-
-// Our schema with block specs, which contain the configs and implementations for blocks
-// that we want our editor to use.
-const schema = BlockNoteSchema.create({
- blockSpecs: {
- // Adds all default blocks.
- ...defaultBlockSpecs,
-
- // Adds the code block.
- run: Run,
- directory: Directory,
- },
-});
-
-// Slash menu item to insert an Alert block
-const insertRun = (editor: typeof schema.BlockNoteEditor) => ({
- title: "Code",
- onItemClick: () => {
- insertOrUpdateBlock(editor, {
- type: "run",
- });
- },
- icon: <CodeIcon size={18} />,
- aliases: ["code", "run"],
- group: "Execute",
-});
-
-const insertDirectory = (editor: typeof schema.BlockNoteEditor) => ({
- title: "Directory",
- onItemClick: () => {
- insertOrUpdateBlock(editor, {
- type: "directory",
- });
- },
- icon: <FolderOpenIcon size={18} />,
- aliases: ["directory", "dir", "folder"],
- group: "Execute",
-});
-
-export default function Editor() {
- const runbookId = useStore((store: AtuinState) => store.currentRunbook);
- const refreshRunbooks = useStore(
- (store: AtuinState) => store.refreshRunbooks,
- );
- let [runbook, setRunbook] = useState<Runbook | null>(null);
-
- useEffect(() => {
- if (!runbookId) return;
-
- const fetchRunbook = async () => {
- let rb = await Runbook.load(runbookId);
-
- setRunbook(rb);
- };
-
- fetchRunbook();
- }, [runbookId]);
-
- const onChange = async () => {
- if (!runbook) return;
-
- console.log("saved!");
- runbook.name = fetchName();
- if (editor) runbook.content = JSON.stringify(editor.document);
-
- await runbook.save();
- refreshRunbooks();
- };
-
- const debouncedOnChange = useDebounceCallback(onChange, 1000);
-
- const editor = useMemo(() => {
- if (!runbook) return undefined;
- if (runbook.content) {
- return BlockNoteEditor.create({
- initialContent: JSON.parse(runbook.content),
- schema,
- });
- }
-
- return BlockNoteEditor.create({ schema });
- }, [runbook]);
-
- const fetchName = (): string => {
- // Infer the title from the first text block
- if (!editor) return "Untitled";
-
- let blocks = editor.document;
- for (const block of blocks) {
- if (block.type == "heading" || block.type == "paragraph") {
- if (block.content.length == 0) continue;
- // @ts-ignore
- if (block.content[0].text.length == 0) continue;
-
- // @ts-ignore
- return block.content[0].text;
- }
- }
-
- return "Untitled";
- };
-
- if (!runbook) {
- return (
- <div className="flex w-full h-full flex-col justify-center items-center">
- <Spinner />
- </div>
- );
- }
-
- if (editor === undefined) {
- return (
- <div className="flex w-full h-full flex-col justify-center items-center">
- <Spinner />
- </div>
- );
- }
-
- // Renders the editor instance.
- return (
- <div className="overflow-y-scroll w-full">
- <BlockNoteView
- editor={editor}
- slashMenu={false}
- sideMenu={false}
- onChange={debouncedOnChange}
- >
- <SuggestionMenuController
- triggerCharacter={"/"}
- getItems={async (query: any) =>
- filterSuggestionItems(
- [
- ...getDefaultReactSlashMenuItems(editor),
- insertRun(editor),
- insertDirectory(editor),
- ],
- query,
- )
- }
- />
-
- <SideMenuController
- sideMenu={(props: any) => (
- <SideMenu {...props}>
- <AddBlockButton {...props} />
- <DeleteBlock {...props} />
- </SideMenu>
- )}
- />
- </BlockNoteView>
- </div>
- );
-}
diff --git a/ui/src/components/runbooks/editor/blocks/Directory/index.tsx b/ui/src/components/runbooks/editor/blocks/Directory/index.tsx
deleted file mode 100644
index 3e4f93d9..00000000
--- a/ui/src/components/runbooks/editor/blocks/Directory/index.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import { useState } from "react";
-import { Input, Tooltip, Button } from "@nextui-org/react";
-import { FolderInputIcon } from "lucide-react";
-
-// @ts-ignore
-import { createReactBlockSpec } from "@blocknote/react";
-
-import { open } from "@tauri-apps/plugin-dialog";
-
-interface DirectoryProps {
- path: string;
- onInputChange: (val: string) => void;
-}
-
-const Directory = ({ path, onInputChange }: DirectoryProps) => {
- const [value, setValue] = useState(path);
-
- const selectFolder = async () => {
- const path = await open({
- multiple: false,
- directory: true,
- });
-
- setValue(path || "");
- onInputChange(path || "");
- };
-
- return (
- <div className="w-full !max-w-full !outline-none overflow-none">
- <Tooltip
- content="Change working directory for all subsequent code blocks"
- delay={1000}
- >
- <div className="flex flex-row">
- <div className="mr-2">
- <Button
- isIconOnly
- variant="flat"
- aria-label="Select folder"
- onPress={selectFolder}
- >
- <FolderInputIcon />
- </Button>
- </div>
-
- <div className="w-full">
- <Input
- placeholder="~"
- value={value}
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- onValueChange={(val) => {
- setValue(val);
- onInputChange(val);
- }}
- />
- </div>
- </div>
- </Tooltip>
- </div>
- );
-};
-
-export default createReactBlockSpec(
- {
- type: "directory",
- propSchema: {
- path: { default: "" },
- },
- content: "none",
- },
- {
- // @ts-ignore
- render: ({ block, editor, code, type }) => {
- const onInputChange = (val: string) => {
- editor.updateBlock(block, {
- // @ts-ignore
- props: { ...block.props, path: val },
- });
- };
-
- return (
- <Directory path={block.props.path} onInputChange={onInputChange} />
- );
- },
- },
-);
diff --git a/ui/src/components/runbooks/editor/blocks/Run/extensions.ts b/ui/src/components/runbooks/editor/blocks/Run/extensions.ts
deleted file mode 100644
index 76fc4343..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/extensions.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-// Based on the basicSetup extension, as suggested by the source. Customized for Atuin.
-
-import {
- KeyBinding,
- lineNumbers,
- highlightActiveLineGutter,
- highlightSpecialChars,
- drawSelection,
- dropCursor,
- rectangularSelection,
- crosshairCursor,
- highlightActiveLine,
- keymap,
-} from "@codemirror/view";
-import { EditorState, Extension } from "@codemirror/state";
-import { history, defaultKeymap, historyKeymap } from "@codemirror/commands";
-import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
-
-import {
- closeBrackets,
- autocompletion,
- closeBracketsKeymap,
- completionKeymap,
- CompletionContext,
-} from "@codemirror/autocomplete";
-
-import {
- foldGutter,
- indentOnInput,
- syntaxHighlighting,
- defaultHighlightStyle,
- bracketMatching,
- indentUnit,
- foldKeymap,
-} from "@codemirror/language";
-
-import { lintKeymap } from "@codemirror/lint";
-import { invoke } from "@tauri-apps/api/core";
-
-export interface MinimalSetupOptions {
- highlightSpecialChars?: boolean;
- history?: boolean;
- drawSelection?: boolean;
- syntaxHighlighting?: boolean;
-
- defaultKeymap?: boolean;
- historyKeymap?: boolean;
-}
-
-export interface BasicSetupOptions extends MinimalSetupOptions {
- lineNumbers?: boolean;
- highlightActiveLineGutter?: boolean;
- foldGutter?: boolean;
- dropCursor?: boolean;
- allowMultipleSelections?: boolean;
- indentOnInput?: boolean;
- bracketMatching?: boolean;
- closeBrackets?: boolean;
- autocompletion?: boolean;
- rectangularSelection?: boolean;
- crosshairCursor?: boolean;
- highlightActiveLine?: boolean;
- highlightSelectionMatches?: boolean;
-
- closeBracketsKeymap?: boolean;
- searchKeymap?: boolean;
- foldKeymap?: boolean;
- completionKeymap?: boolean;
- lintKeymap?: boolean;
- tabSize?: number;
-}
-
-function myCompletions(context: CompletionContext) {
- let word = context.matchBefore(/^.*/);
-
- if (!word) return null;
- if (word.from == word.to && !context.explicit) return null;
-
- return invoke("prefix_search", { query: word.text }).then(
- // @ts-ignore
- (results: string[]) => {
- let options = results.map((i) => {
- return { label: i, type: "text" };
- });
-
- return {
- from: word.from,
- options,
- };
- },
- );
-}
-
-const buildAutocomplete = (): Extension => {
- let ac = autocompletion({ override: [myCompletions] });
-
- return ac;
-};
-
-export const extensions = (options: BasicSetupOptions = {}): Extension[] => {
- const { crosshairCursor: initCrosshairCursor = false } = options;
-
- let keymaps: KeyBinding[] = [];
- if (options.closeBracketsKeymap !== false) {
- keymaps = keymaps.concat(closeBracketsKeymap);
- }
- if (options.defaultKeymap !== false) {
- keymaps = keymaps.concat(defaultKeymap);
- }
- if (options.searchKeymap !== false) {
- keymaps = keymaps.concat(searchKeymap);
- }
- if (options.historyKeymap !== false) {
- keymaps = keymaps.concat(historyKeymap);
- }
- if (options.foldKeymap !== false) {
- keymaps = keymaps.concat(foldKeymap);
- }
- if (options.completionKeymap !== false) {
- keymaps = keymaps.concat(completionKeymap);
- }
- if (options.lintKeymap !== false) {
- keymaps = keymaps.concat(lintKeymap);
- }
- const extensions: Extension[] = [];
- if (options.lineNumbers !== false) extensions.push(lineNumbers());
- if (options.highlightActiveLineGutter !== false)
- extensions.push(highlightActiveLineGutter());
- if (options.highlightSpecialChars !== false)
- extensions.push(highlightSpecialChars());
- if (options.history !== false) extensions.push(history());
- if (options.foldGutter !== false) extensions.push(foldGutter());
- if (options.drawSelection !== false) extensions.push(drawSelection());
- if (options.dropCursor !== false) extensions.push(dropCursor());
- if (options.allowMultipleSelections !== false)
- extensions.push(EditorState.allowMultipleSelections.of(true));
- if (options.indentOnInput !== false) extensions.push(indentOnInput());
- if (options.syntaxHighlighting !== false)
- extensions.push(
- syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
- );
-
- if (options.bracketMatching !== false) extensions.push(bracketMatching());
- if (options.closeBrackets !== false) extensions.push(closeBrackets());
- if (options.autocompletion !== false) extensions.push(buildAutocomplete());
-
- if (options.rectangularSelection !== false)
- extensions.push(rectangularSelection());
- if (initCrosshairCursor !== false) extensions.push(crosshairCursor());
- if (options.highlightActiveLine !== false)
- extensions.push(highlightActiveLine());
- if (options.highlightSelectionMatches !== false)
- extensions.push(highlightSelectionMatches());
- if (options.tabSize && typeof options.tabSize === "number")
- extensions.push(indentUnit.of(" ".repeat(options.tabSize)));
-
- return extensions.concat([keymap.of(keymaps.flat())]).filter(Boolean);
-};
diff --git a/ui/src/components/runbooks/editor/blocks/Run/index.css b/ui/src/components/runbooks/editor/blocks/Run/index.css
deleted file mode 100644
index e854c03b..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/index.css
+++ /dev/null
@@ -1,9 +0,0 @@
-ProseMirror-focused {
- outline: none !important;
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1) !important;
-}
-
-.cm-editor.cm-focused {
- outline: none !important;
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1) !important;
-}
diff --git a/ui/src/components/runbooks/editor/blocks/Run/index.tsx b/ui/src/components/runbooks/editor/blocks/Run/index.tsx
deleted file mode 100644
index bef083ba..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/index.tsx
+++ /dev/null
@@ -1,229 +0,0 @@
-// @ts-ignore
-import { createReactBlockSpec } from "@blocknote/react";
-
-import "./index.css";
-
-import CodeMirror from "@uiw/react-codemirror";
-import { keymap } from "@codemirror/view";
-import { langs } from "@uiw/codemirror-extensions-langs";
-
-import { Play, Square } from "lucide-react";
-import { useState } from "react";
-
-import { extensions } from "./extensions";
-import { platform } from "@tauri-apps/plugin-os";
-import { invoke } from "@tauri-apps/api/core";
-import Terminal from "./terminal.tsx";
-
-import "@xterm/xterm/css/xterm.css";
-import { AtuinState, useStore } from "@/state/store.ts";
-
-interface RunBlockProps {
- onChange: (val: string) => void;
- onRun?: (pty: string) => void;
- onStop?: (pty: string) => void;
- id: string;
- code: string;
- type: string;
- pty: string;
- isEditable: boolean;
- editor: any;
-}
-
-const findFirstParentOfType = (editor: any, id: string, type: string): any => {
- // TODO: the types for blocknote aren't working. Now I'm doing this sort of shit,
- // really need to fix that.
- const document = editor.document;
- var lastOfType = null;
-
- // Iterate through ALL of the blocks.
- for (let i = 0; i < document.length; i++) {
- if (document[i].id == id) return lastOfType;
-
- if (document[i].type == type) lastOfType = document[i];
- }
-
- return lastOfType;
-};
-
-const RunBlock = ({
- onChange,
- id,
- code,
- isEditable,
- onRun,
- onStop,
- pty,
- editor,
-}: RunBlockProps) => {
- const [value, setValue] = useState<String>(code);
- const cleanupPtyTerm = useStore((store: AtuinState) => store.cleanupPtyTerm);
- const terminals = useStore((store: AtuinState) => store.terminals);
-
- const [currentRunbook, incRunbookPty, decRunbookPty] = useStore(
- (store: AtuinState) => [
- store.currentRunbook,
- store.incRunbookPty,
- store.decRunbookPty,
- ],
- );
-
- const isRunning = pty !== null && pty !== "";
-
- const handleToggle = async (event: any | null) => {
- if (event) event.stopPropagation();
-
- // If there's no code, don't do anything
- if (!value) return;
-
- if (isRunning) {
- await invoke("pty_kill", { pid: pty });
-
- terminals[pty].terminal.dispose();
- cleanupPtyTerm(pty);
-
- if (onStop) onStop(pty);
- if (currentRunbook) decRunbookPty(currentRunbook);
- }
-
- if (!isRunning) {
- let cwd = findFirstParentOfType(editor, id, "directory");
-
- if (cwd) {
- cwd = cwd.props.path;
- } else {
- cwd = "~";
- }
-
- let pty = await invoke<string>("pty_open", { cwd });
- if (onRun) onRun(pty);
-
- if (currentRunbook) incRunbookPty(currentRunbook);
-
- let isWindows = platform() == "windows";
- let cmdEnd = isWindows ? "\r\n" : "\n";
-
- let val = !value.endsWith("\n") ? value + cmdEnd : value;
- await invoke("pty_write", { pid: pty, data: val });
- }
- };
-
- const handleCmdEnter = () => {
- handleToggle(null);
- return true;
- };
-
- const customKeymap = keymap.of([
- {
- key: "Mod-Enter",
- run: handleCmdEnter,
- },
- ]);
-
- return (
- <div className="w-full !max-w-full !outline-none overflow-none">
- <div className="flex flex-row items-start">
- <div className="flex">
- <button
- onClick={handleToggle}
- className={`flex items-center justify-center flex-shrink-0 w-8 h-8 mr-2 rounded border focus:outline-none focus:ring-2 transition-all duration-300 ease-in-out ${
- isRunning
- ? "border-red-200 bg-red-50 text-red-600 hover:bg-red-100 hover:border-red-300 focus:ring-red-300"
- : "border-green-200 bg-green-50 text-green-600 hover:bg-green-100 hover:border-green-300 focus:ring-green-300"
- }`}
- aria-label={isRunning ? "Stop code" : "Run code"}
- >
- <span
- className={`inline-block transition-transform duration-300 ease-in-out ${isRunning ? "rotate-180" : ""}`}
- >
- {isRunning ? <Square size={16} /> : <Play size={16} />}
- </span>
- </button>
- </div>
- <div className="flex-1 min-w-0 w-40">
- <CodeMirror
- id={id}
- placeholder={"Write your code here..."}
- className="!pt-0 max-w-full border border-gray-300 rounded"
- value={code}
- editable={isEditable}
- autoFocus
- onChange={(val) => {
- setValue(val);
- onChange(val);
- }}
- extensions={[customKeymap, ...extensions(), langs.shell()]}
- basicSetup={false}
- />
- <div
- className={`overflow-hidden transition-all duration-300 ease-in-out min-w-0 ${
- isRunning ? "block" : "hidden"
- }`}
- >
- {pty && <Terminal pty={pty} />}
- </div>
- </div>
- </div>
- </div>
- );
-};
-
-export default createReactBlockSpec(
- {
- type: "run",
- propSchema: {
- type: {
- default: "bash",
- },
- code: { default: "" },
- pty: { default: "" },
- global: { default: false },
- },
- content: "none",
- },
- {
- // @ts-ignore
- render: ({ block, editor, code, type }) => {
- const onInputChange = (val: string) => {
- editor.updateBlock(block, {
- // @ts-ignore
- props: { ...block.props, code: val },
- });
- };
-
- const onRun = (pty: string) => {
- editor.updateBlock(block, {
- // @ts-ignore
- props: { ...block.props, pty: pty },
- });
- };
-
- const onStop = (_pty: string) => {
- editor?.updateBlock(block, {
- props: { ...block.props, pty: "" },
- });
- };
-
- return (
- <RunBlock
- onChange={onInputChange}
- id={block?.id}
- code={block.props.code}
- type={block.props.type}
- pty={block.props.pty}
- isEditable={editor.isEditable}
- onRun={onRun}
- onStop={onStop}
- editor={editor}
- />
- );
- },
- toExternalHTML: ({ block }) => {
- return (
- <pre lang="beep boop">
- <code lang="bash">{block?.props?.code}</code>
- </pre>
- );
- },
- },
-);
diff --git a/ui/src/components/runbooks/editor/blocks/Run/terminal.tsx b/ui/src/components/runbooks/editor/blocks/Run/terminal.tsx
deleted file mode 100644
index a6dc589f..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/terminal.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { useState, useEffect, useRef } from "react";
-import { listen } from "@tauri-apps/api/event";
-import "@xterm/xterm/css/xterm.css";
-import { useStore } from "@/state/store";
-import { invoke } from "@tauri-apps/api/core";
-import { IDisposable } from "@xterm/xterm";
-
-const usePersistentTerminal = (pty: string) => {
- const newPtyTerm = useStore((store) => store.newPtyTerm);
- const terminals = useStore((store) => store.terminals);
- const [isReady, setIsReady] = useState(false);
-
- useEffect(() => {
- if (!terminals.hasOwnProperty(pty)) {
- // create a new terminal and store it in the store.
- // this means we can resume the same instance even across mount/dismount
- newPtyTerm(pty);
- }
-
- setIsReady(true);
-
- return () => {
- // We don't dispose of the terminal when the component unmounts
- };
- }, [pty, terminals, newPtyTerm]);
-
- return { terminalData: terminals[pty], isReady };
-};
-
-const TerminalComponent = ({ pty }: any) => {
- const terminalRef = useRef(null);
- const { terminalData, isReady } = usePersistentTerminal(pty);
- const [isAttached, setIsAttached] = useState(false);
- const cleanupListenerRef = useRef<(() => void) | null>(null);
- const keyDispose = useRef<IDisposable | null>(null);
-
- useEffect(() => {
- // no pty? no terminal
- if (pty == null) return;
-
- // the terminal may still be being created so hold off
- if (!isReady) return;
-
- const windowResize = () => {
- if (!terminalData || !terminalData.fitAddon) return;
-
- terminalData.fitAddon.fit();
- };
-
- // terminal object needs attaching to a ref to a div
- if (!isAttached && terminalData && terminalData.terminal) {
- // If it's never been attached, attach it
- if (!terminalData.terminal.element && terminalRef.current) {
- terminalData.terminal.open(terminalRef.current);
-
- // it might have been previously attached, but need moving elsewhere
- } else if (terminalData && terminalRef.current) {
- // @ts-ignore
- terminalRef.current.appendChild(terminalData.terminal.element);
- }
-
- terminalData.fitAddon.fit();
- setIsAttached(true);
-
- window.addEventListener("resize", windowResize);
-
- const disposeOnKey = terminalData.terminal.onKey(async (event) => {
- await invoke("pty_write", { pid: pty, data: event.key });
- });
-
- keyDispose.current = disposeOnKey;
- }
-
- listen(`pty-${pty}`, (event: any) => {
- terminalData.terminal.write(event.payload);
- }).then((ul) => {
- cleanupListenerRef.current = ul;
- });
-
- // Customize further as needed
- return () => {
- if (
- terminalData &&
- terminalData.terminal &&
- terminalData.terminal.element
- ) {
- // Instead of removing, we just detach
- if (terminalData.terminal.element.parentElement) {
- terminalData.terminal.element.parentElement.removeChild(
- terminalData.terminal.element,
- );
- }
- setIsAttached(false);
- }
-
- if (cleanupListenerRef.current) {
- cleanupListenerRef.current();
- }
-
- if (keyDispose.current) keyDispose.current.dispose();
-
- window.removeEventListener("resize", windowResize);
- };
- }, [terminalData, isReady]);
-
- if (!isReady) return null;
-
- return (
- <div className="!max-w-full min-w-0 overflow-hidden" ref={terminalRef} />
- );
-};
-
-export default TerminalComponent;
diff --git a/ui/src/components/runbooks/editor/index.css b/ui/src/components/runbooks/editor/index.css
deleted file mode 100644
index 067cc500..00000000
--- a/ui/src/components/runbooks/editor/index.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.editor a {
- color: #0000ee;
-}
-
-.editor a:hover {
- cursor: pointer;
-}
diff --git a/ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx b/ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx
deleted file mode 100644
index 84a9f5c8..00000000
--- a/ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import {
- SideMenuProps,
- useBlockNoteEditor,
- useComponentsContext,
-} from "@blocknote/react";
-import { TrashIcon } from "lucide-react";
-
-// Custom Side Menu button to remove the hovered block.
-export function DeleteBlock(props: SideMenuProps) {
- const editor = useBlockNoteEditor();
-
- const Components = useComponentsContext()!;
-
- return (
- <Components.SideMenu.Button
- label="Remove block"
- className="mx-1"
- icon={
- <TrashIcon
- size={24}
- onClick={() => {
- editor.removeBlocks([props.block]);
- }}
- />
- }
- />
- );
-}
diff --git a/ui/src/components/ui/alert.tsx b/ui/src/components/ui/alert.tsx
deleted file mode 100644
index 41fa7e05..00000000
--- a/ui/src/components/ui/alert.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import * as React from "react"
-import { cva, type VariantProps } from "class-variance-authority"
-
-import { cn } from "@/lib/utils"
-
-const alertVariants = cva(
- "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
- {
- variants: {
- variant: {
- default: "bg-background text-foreground",
- destructive:
- "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- }
-)
-
-const Alert = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
->(({ className, variant, ...props }, ref) => (
- <div
- ref={ref}
- role="alert"
- className={cn(alertVariants({ variant }), className)}
- {...props}
- />
-))
-Alert.displayName = "Alert"
-
-const AlertTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLHeadingElement>
->(({ className, ...props }, ref) => (
- <h5
- ref={ref}
- className={cn("mb-1 font-medium leading-none tracking-tight", className)}
- {...props}
- />
-))
-AlertTitle.displayName = "AlertTitle"
-
-const AlertDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("text-sm [&_p]:leading-relaxed", className)}
- {...props}
- />
-))
-AlertDescription.displayName = "AlertDescription"
-
-export { Alert, AlertTitle, AlertDescription }
diff --git a/ui/src/components/ui/button.tsx b/ui/src/components/ui/button.tsx
deleted file mode 100644
index 0ba42773..00000000
--- a/ui/src/components/ui/button.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import * as React from "react"
-import { Slot } from "@radix-ui/react-slot"
-import { cva, type VariantProps } from "class-variance-authority"
-
-import { cn } from "@/lib/utils"
-
-const buttonVariants = cva(
- "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
- {
- variants: {
- variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
- destructive:
- "bg-destructive text-destructive-foreground hover:bg-destructive/90",
- outline:
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
- secondary:
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-10 px-4 py-2",
- sm: "h-9 rounded-md px-3",
- lg: "h-11 rounded-md px-8",
- icon: "h-10 w-10",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- }
-)
-
-export interface ButtonProps
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
- VariantProps<typeof buttonVariants> {
- asChild?: boolean
-}
-
-const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
- ({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button"
- return (
- <Comp
- className={cn(buttonVariants({ variant, size, className }))}
- ref={ref}
- {...props}
- />
- )
- }
-)
-Button.displayName = "Button"
-
-export { Button, buttonVariants }
diff --git a/ui/src/components/ui/card.tsx b/ui/src/components/ui/card.tsx
deleted file mode 100644
index afa13ecf..00000000
--- a/ui/src/components/ui/card.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import * as React from "react"
-
-import { cn } from "@/lib/utils"
-
-const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn(
- "rounded-lg border bg-card text-card-foreground shadow-sm",
- className
- )}
- {...props}
- />
-))
-Card.displayName = "Card"
-
-const CardHeader = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("flex flex-col space-y-1.5 p-6", className)}
- {...props}
- />
-))
-CardHeader.displayName = "CardHeader"
-
-const CardTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLHeadingElement>
->(({ className, ...props }, ref) => (
- <h3
- ref={ref}
- className={cn(
- "text-2xl font-semibold leading-none tracking-tight",
- className
- )}
- {...props}
- />
-))
-CardTitle.displayName = "CardTitle"
-
-const CardDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
->(({ className, ...props }, ref) => (
- <p
- ref={ref}
- className={cn("text-sm text-muted-foreground", className)}
- {...props}
- />
-))
-CardDescription.displayName = "CardDescription"
-
-const CardContent = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
-))
-CardContent.displayName = "CardContent"
-
-const CardFooter = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("flex items-center p-6 pt-0", className)}
- {...props}
- />
-))
-CardFooter.displayName = "CardFooter"
-
-export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/ui/src/components/ui/chart.tsx b/ui/src/components/ui/chart.tsx
deleted file mode 100644
index a21d77ee..00000000
--- a/ui/src/components/ui/chart.tsx
+++ /dev/null
@@ -1,363 +0,0 @@
-import * as React from "react"
-import * as RechartsPrimitive from "recharts"
-
-import { cn } from "@/lib/utils"
-
-// Format: { THEME_NAME: CSS_SELECTOR }
-const THEMES = { light: "", dark: ".dark" } as const
-
-export type ChartConfig = {
- [k in string]: {
- label?: React.ReactNode
- icon?: React.ComponentType
- } & (
- | { color?: string; theme?: never }
- | { color?: never; theme: Record<keyof typeof THEMES, string> }
- )
-}
-
-type ChartContextProps = {
- config: ChartConfig
-}
-
-const ChartContext = React.createContext<ChartContextProps | null>(null)
-
-function useChart() {
- const context = React.useContext(ChartContext)
-
- if (!context) {
- throw new Error("useChart must be used within a <ChartContainer />")
- }
-
- return context
-}
-
-const ChartContainer = React.forwardRef<
- HTMLDivElement,
- React.ComponentProps<"div"> & {
- config: ChartConfig
- children: React.ComponentProps<
- typeof RechartsPrimitive.ResponsiveContainer
- >["children"]
- }
->(({ id, className, children, config, ...props }, ref) => {
- const uniqueId = React.useId()
- const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
-
- return (
- <ChartContext.Provider value={{ config }}>
- <div
- data-chart={chartId}
- ref={ref}
- className={cn(
- "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
- className
- )}
- {...props}
- >
- <ChartStyle id={chartId} config={config} />
- <RechartsPrimitive.ResponsiveContainer>
- {children}
- </RechartsPrimitive.ResponsiveContainer>
- </div>
- </ChartContext.Provider>
- )
-})
-ChartContainer.displayName = "Chart"
-
-const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
- const colorConfig = Object.entries(config).filter(
- ([_, config]) => config.theme || config.color
- )
-
- if (!colorConfig.length) {
- return null
- }
-
- return (
- <style
- dangerouslySetInnerHTML={{
- __html: Object.entries(THEMES)
- .map(
- ([theme, prefix]) => `
-${prefix} [data-chart=${id}] {
-${colorConfig
- .map(([key, itemConfig]) => {
- const color =
- itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
- itemConfig.color
- return color ? ` --color-${key}: ${color};` : null
- })
- .join("\n")}
-}
-`
- )
- .join("\n"),
- }}
- />
- )
-}
-
-const ChartTooltip = RechartsPrimitive.Tooltip
-
-const ChartTooltipContent = React.forwardRef<
- HTMLDivElement,
- React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
- React.ComponentProps<"div"> & {
- hideLabel?: boolean
- hideIndicator?: boolean
- indicator?: "line" | "dot" | "dashed"
- nameKey?: string
- labelKey?: string
- }
->(
- (
- {
- active,
- payload,
- className,
- indicator = "dot",
- hideLabel = false,
- hideIndicator = false,
- label,
- labelFormatter,
- labelClassName,
- formatter,
- color,
- nameKey,
- labelKey,
- },
- ref
- ) => {
- const { config } = useChart()
-
- const tooltipLabel = React.useMemo(() => {
- if (hideLabel || !payload?.length) {
- return null
- }
-
- const [item] = payload
- const key = `${labelKey || item.dataKey || item.name || "value"}`
- const itemConfig = getPayloadConfigFromPayload(config, item, key)
- const value =
- !labelKey && typeof label === "string"
- ? config[label as keyof typeof config]?.label || label
- : itemConfig?.label
-
- if (labelFormatter) {
- return (
- <div className={cn("font-medium", labelClassName)}>
- {labelFormatter(value, payload)}
- </div>
- )
- }
-
- if (!value) {
- return null
- }
-
- return <div className={cn("font-medium", labelClassName)}>{value}</div>
- }, [
- label,
- labelFormatter,
- payload,
- hideLabel,
- labelClassName,
- config,
- labelKey,
- ])
-
- if (!active || !payload?.length) {
- return null
- }
-
- const nestLabel = payload.length === 1 && indicator !== "dot"
-
- return (
- <div
- ref={ref}
- className={cn(
- "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
- className
- )}
- >
- {!nestLabel ? tooltipLabel : null}
- <div className="grid gap-1.5">
- {payload.map((item, index) => {
- const key = `${nameKey || item.name || item.dataKey || "value"}`
- const itemConfig = getPayloadConfigFromPayload(config, item, key)
- const indicatorColor = color || item.payload.fill || item.color
-
- return (
- <div
- key={item.dataKey}
- className={cn(
- "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
- indicator === "dot" && "items-center"
- )}
- >
- {formatter && item?.value !== undefined && item.name ? (
- formatter(item.value, item.name, item, index, item.payload)
- ) : (
- <>
- {itemConfig?.icon ? (
- <itemConfig.icon />
- ) : (
- !hideIndicator && (
- <div
- className={cn(
- "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
- {
- "h-2.5 w-2.5": indicator === "dot",
- "w-1": indicator === "line",
- "w-0 border-[1.5px] border-dashed bg-transparent":
- indicator === "dashed",
- "my-0.5": nestLabel && indicator === "dashed",
- }
- )}
- style={
- {
- "--color-bg": indicatorColor,
- "--color-border": indicatorColor,
- } as React.CSSProperties
- }
- />
- )
- )}
- <div
- className={cn(
- "flex flex-1 justify-between leading-none",
- nestLabel ? "items-end" : "items-center"
- )}
- >
- <div className="grid gap-1.5">
- {nestLabel ? tooltipLabel : null}
- <span className="text-muted-foreground">
- {itemConfig?.label || item.name}
- </span>
- </div>
- {item.value && (
- <span className="font-mono font-medium tabular-nums text-foreground">
- {item.value.toLocaleString()}
- </span>
- )}
- </div>
- </>
- )}
- </div>
- )
- })}
- </div>
- </div>
- )
- }
-)
-ChartTooltipContent.displayName = "ChartTooltip"
-
-const ChartLegend = RechartsPrimitive.Legend
-
-const ChartLegendContent = React.forwardRef<
- HTMLDivElement,
- React.ComponentProps<"div"> &
- Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
- hideIcon?: boolean
- nameKey?: string
- }
->(
- (
- { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
- ref
- ) => {
- const { config } = useChart()
-
- if (!payload?.length) {
- return null
- }
-
- return (
- <div
- ref={ref}
- className={cn(
- "flex items-center justify-center gap-4",
- verticalAlign === "top" ? "pb-3" : "pt-3",
- className
- )}
- >
- {payload.map((item) => {
- const key = `${nameKey || item.dataKey || "value"}`
- const itemConfig = getPayloadConfigFromPayload(config, item, key)
-
- return (
- <div
- key={item.value}
- className={cn(
- "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
- )}
- >
- {itemConfig?.icon && !hideIcon ? (
- <itemConfig.icon />
- ) : (
- <div
- className="h-2 w-2 shrink-0 rounded-[2px]"
- style={{
- backgroundColor: item.color,
- }}
- />
- )}
- {itemConfig?.label}
- </div>
- )
- })}
- </div>
- )
- }
-)
-ChartLegendContent.displayName = "ChartLegend"
-
-// Helper to extract item config from a payload.
-function getPayloadConfigFromPayload(
- config: ChartConfig,
- payload: unknown,
- key: string
-) {
- if (typeof payload !== "object" || payload === null) {
- return undefined
- }
-
- const payloadPayload =
- "payload" in payload &&
- typeof payload.payload === "object" &&
- payload.payload !== null
- ? payload.payload
- : undefined
-
- let configLabelKey: string = key
-
- if (
- key in payload &&
- typeof payload[key as keyof typeof payload] === "string"
- ) {
- configLabelKey = payload[key as keyof typeof payload] as string
- } else if (
- payloadPayload &&
- key in payloadPayload &&
- typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
- ) {
- configLabelKey = payloadPayload[
- key as keyof typeof payloadPayload
- ] as string
- }
-
- return configLabelKey in config
- ? config[configLabelKey]
- : config[key as keyof typeof config]
-}
-
-export {
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
- ChartLegend,
- ChartLegendContent,
- ChartStyle,
-}
diff --git a/ui/src/components/ui/data-table.tsx b/ui/src/components/ui/data-table.tsx
deleted file mode 100644
index cf96b620..00000000
--- a/ui/src/components/ui/data-table.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-"use client";
-
-import {
- ColumnDef,
- flexRender,
- getCoreRowModel,
- useReactTable,
-} from "@tanstack/react-table";
-
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
-
-interface DataTableProps<TData, TValue> {
- columns: ColumnDef<TData, TValue>[];
- data: TData[];
-}
-
-export default function DataTable<TData, TValue>({
- columns,
- data,
-}: DataTableProps<TData, TValue>) {
- const table = useReactTable({
- data,
- columns,
- getCoreRowModel: getCoreRowModel(),
- });
-
- return (
- <div className="rounded-md border">
- <Table>
- <TableHeader>
- {table.getHeaderGroups().map((headerGroup) => (
- <TableRow key={headerGroup.id}>
- {headerGroup.headers.map((header) => {
- return (
- <TableHead key={header.id}>
- {header.isPlaceholder
- ? null
- : flexRender(
- header.column.columnDef.header,
- header.getContext(),
- )}
- </TableHead>
- );
- })}
- </TableRow>
- ))}
- </TableHeader>
- <TableBody>
- {table.getRowModel().rows?.length ? (
- table.getRowModel().rows.map((row) => (
- <TableRow
- key={row.id}
- data-state={row.getIsSelected() && "selected"}
- >
- {row.getVisibleCells().map((cell) => (
- <TableCell key={cell.id}>
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
- </TableCell>
- ))}
- </TableRow>
- ))
- ) : (
- <TableRow>
- <TableCell colSpan={columns.length} className="h-24 text-center">
- No results.
- </TableCell>
- </TableRow>
- )}
- </TableBody>
- </Table>
- </div>
- );
-}
diff --git a/ui/src/components/ui/dialog.tsx b/ui/src/components/ui/dialog.tsx
deleted file mode 100644
index c23630eb..00000000
--- a/ui/src/components/ui/dialog.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import * as React from "react"
-import * as DialogPrimitive from "@radix-ui/react-dialog"
-import { X } from "lucide-react"
-
-import { cn } from "@/lib/utils"
-
-const Dialog = DialogPrimitive.Root
-
-const DialogTrigger = DialogPrimitive.Trigger
-
-const DialogPortal = DialogPrimitive.Portal
-
-const DialogClose = DialogPrimitive.Close
-
-const DialogOverlay = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Overlay>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
->(({ className, ...props }, ref) => (
- <DialogPrimitive.Overlay
- ref={ref}
- className={cn(
- "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
- className
- )}
- {...props}
- />
-))
-DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
-
-const DialogContent = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
->(({ className, children, ...props }, ref) => (
- <DialogPortal>
- <DialogOverlay />
- <DialogPrimitive.Content
- ref={ref}
- className={cn(
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
- className
- )}
- {...props}
- >
- {children}
- <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
- <X className="h-4 w-4" />
- <span className="sr-only">Close</span>
- </DialogPrimitive.Close>
- </DialogPrimitive.Content>
- </DialogPortal>
-))
-DialogContent.displayName = DialogPrimitive.Content.displayName
-
-const DialogHeader = ({
- className,
- ...props
-}: React.HTMLAttributes<HTMLDivElement>) => (
- <div
- className={cn(
- "flex flex-col space-y-1.5 text-center sm:text-left",
- className
- )}
- {...props}
- />
-)
-DialogHeader.displayName = "DialogHeader"
-
-const DialogFooter = ({
- className,
- ...props
-}: React.HTMLAttributes<HTMLDivElement>) => (
- <div
- className={cn(
- "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
- className
- )}
- {...props}
- />
-)
-DialogFooter.displayName = "DialogFooter"
-
-const DialogTitle = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Title>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
->(({ className, ...props }, ref) => (
- <DialogPrimitive.Title
- ref={ref}
- className={cn(
- "text-lg font-semibold leading-none tracking-tight",
- className
- )}
- {...props}
- />
-))
-DialogTitle.displayName = DialogPrimitive.Title.displayName
-
-const DialogDescription = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Description>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
->(({ className, ...props }, ref) => (
- <DialogPrimitive.Description
- ref={ref}
- className={cn("text-sm text-muted-foreground", className)}
- {...props}
- />
-))
-DialogDescription.displayName = DialogPrimitive.Description.displayName
-
-export {
- Dialog,
- DialogPortal,
- DialogOverlay,
- DialogClose,
- DialogTrigger,
- DialogContent,
- DialogHeader,
- DialogFooter,
- DialogTitle,
- DialogDescription,
-}
diff --git a/ui/src/components/ui/dropdown-menu.tsx b/ui/src/components/ui/dropdown-menu.tsx
deleted file mode 100644
index 769ff7aa..00000000
--- a/ui/src/components/ui/dropdown-menu.tsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import * as React from "react"
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
-import { Check, ChevronRight, Circle } from "lucide-react"
-
-import { cn } from "@/lib/utils"
-
-const DropdownMenu = DropdownMenuPrimitive.Root
-
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
-
-const DropdownMenuGroup = DropdownMenuPrimitive.Group
-
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal
-
-const DropdownMenuSub = DropdownMenuPrimitive.Sub
-
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
-
-const DropdownMenuSubTrigger = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
- inset?: boolean
- }
->(({ className, inset, children, ...props }, ref) => (
- <DropdownMenuPrimitive.SubTrigger
- ref={ref}
- className={cn(
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
- inset && "pl-8",
- className
- )}
- {...props}
- >
- {children}
- <ChevronRight className="ml-auto h-4 w-4" />
- </DropdownMenuPrimitive.SubTrigger>
-))
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName
-
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
->(({ className, ...props }, ref) => (
- <DropdownMenuPrimitive.SubContent
- ref={ref}
- className={cn(
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
- className
- )}
- {...props}
- />
-))
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName
-
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
->(({ className, sideOffset = 4, ...props }, ref) => (
- <DropdownMenuPrimitive.Portal>
- <DropdownMenuPrimitive.Content
- ref={ref}
- sideOffset={sideOffset}
- className={cn(
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
- className
- )}
- {...props}
- />
- </DropdownMenuPrimitive.Portal>
-))
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
-
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Item>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
- inset?: boolean
- }
->(({ className, inset, ...props }, ref) => (
- <DropdownMenuPrimitive.Item
- ref={ref}
- className={cn(
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- inset && "pl-8",
- className
- )}
- {...props}
- />
-))
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
-
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
->(({ className, children, checked, ...props }, ref) => (
- <DropdownMenuPrimitive.CheckboxItem
- ref={ref}
- className={cn(
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- className
- )}
- checked={checked}
- {...props}
- >
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <DropdownMenuPrimitive.ItemIndicator>
- <Check className="h-4 w-4" />
- </DropdownMenuPrimitive.ItemIndicator>
- </span>
- {children}
- </DropdownMenuPrimitive.CheckboxItem>
-))
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName
-
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
->(({ className, children, ...props }, ref) => (
- <DropdownMenuPrimitive.RadioItem
- ref={ref}
- className={cn(
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- className
- )}
- {...props}
- >
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <DropdownMenuPrimitive.ItemIndicator>
- <Circle className="h-2 w-2 fill-current" />
- </DropdownMenuPrimitive.ItemIndicator>
- </span>
- {children}
- </DropdownMenuPrimitive.RadioItem>
-))
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
-
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Label>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
- inset?: boolean
- }
->(({ className, inset, ...props }, ref) => (
- <DropdownMenuPrimitive.Label
- ref={ref}
- className={cn(
- "px-2 py-1.5 text-sm font-semibold",
- inset && "pl-8",
- className
- )}
- {...props}
- />
-))
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
-
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
->(({ className, ...props }, ref) => (
- <DropdownMenuPrimitive.Separator
- ref={ref}
- className={cn("-mx-1 my-1 h-px bg-muted", className)}
- {...props}
- />
-))
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
-
-const DropdownMenuShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes<HTMLSpanElement>) => {
- return (
- <span
- className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
- {...props}
- />
- )
-}
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
-
-export {
- DropdownMenu,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
-}
diff --git a/ui/src/components/ui/table.tsx b/ui/src/components/ui/table.tsx
deleted file mode 100644
index 7f3502f8..00000000
--- a/ui/src/components/ui/table.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import * as React from "react"
-
-import { cn } from "@/lib/utils"
-
-const Table = React.forwardRef<
- HTMLTableElement,
- React.HTMLAttributes<HTMLTableElement>
->(({ className, ...props }, ref) => (
- <div className="relative w-full overflow-auto">
- <table
- ref={ref}
- className={cn("w-full caption-bottom text-sm", className)}
- {...props}
- />
- </div>
-))
-Table.displayName = "Table"
-
-const TableHeader = React.forwardRef<
- HTMLTableSectionElement,
- React.HTMLAttributes<HTMLTableSectionElement>
->(({ className, ...props }, ref) => (
- <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
-))
-TableHeader.displayName = "TableHeader"
-
-const TableBody = React.forwardRef<
- HTMLTableSectionElement,
- React.HTMLAttributes<HTMLTableSectionElement>
->(({ className, ...props }, ref) => (
- <tbody
- ref={ref}
- className={cn("[&_tr:last-child]:border-0", className)}
- {...props}
- />
-))
-TableBody.displayName = "TableBody"
-
-const TableFooter = React.forwardRef<
- HTMLTableSectionElement,
- React.HTMLAttributes<HTMLTableSectionElement>
->(({ className, ...props }, ref) => (
- <tfoot
- ref={ref}
- className={cn(
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
- className
- )}
- {...props}
- />
-))
-TableFooter.displayName = "TableFooter"
-
-const TableRow = React.forwardRef<
- HTMLTableRowElement,
- React.HTMLAttributes<HTMLTableRowElement>
->(({ className, ...props }, ref) => (
- <tr
- ref={ref}
- className={cn(
- "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
- className
- )}
- {...props}
- />
-))
-TableRow.displayName = "TableRow"
-
-const TableHead = React.forwardRef<
- HTMLTableCellElement,
- React.ThHTMLAttributes<HTMLTableCellElement>
->(({ className, ...props }, ref) => (
- <th
- ref={ref}
- className={cn(
- "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
- className
- )}
- {...props}
- />
-))
-TableHead.displayName = "TableHead"
-
-const TableCell = React.forwardRef<
- HTMLTableCellElement,
- React.TdHTMLAttributes<HTMLTableCellElement>
->(({ className, ...props }, ref) => (
- <td
- ref={ref}
- className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
- {...props}
- />
-))
-TableCell.displayName = "TableCell"
-
-const TableCaption = React.forwardRef<
- HTMLTableCaptionElement,
- React.HTMLAttributes<HTMLTableCaptionElement>
->(({ className, ...props }, ref) => (
- <caption
- ref={ref}
- className={cn("mt-4 text-sm text-muted-foreground", className)}
- {...props}
- />
-))
-TableCaption.displayName = "TableCaption"
-
-export {
- Table,
- TableHeader,
- TableBody,
- TableFooter,
- TableHead,
- TableRow,
- TableCell,
- TableCaption,
-}
diff --git a/ui/src/components/ui/toast.tsx b/ui/src/components/ui/toast.tsx
deleted file mode 100644
index a8224775..00000000
--- a/ui/src/components/ui/toast.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import * as React from "react"
-import * as ToastPrimitives from "@radix-ui/react-toast"
-import { cva, type VariantProps } from "class-variance-authority"
-import { X } from "lucide-react"
-
-import { cn } from "@/lib/utils"
-
-const ToastProvider = ToastPrimitives.Provider
-
-const ToastViewport = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Viewport>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Viewport
- ref={ref}
- className={cn(
- "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
- className
- )}
- {...props}
- />
-))
-ToastViewport.displayName = ToastPrimitives.Viewport.displayName
-
-const toastVariants = cva(
- "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
- {
- variants: {
- variant: {
- default: "border bg-background text-foreground",
- destructive:
- "destructive group border-destructive bg-destructive text-destructive-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- }
-)
-
-const Toast = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Root>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
- VariantProps<typeof toastVariants>
->(({ className, variant, ...props }, ref) => {
- return (
- <ToastPrimitives.Root
- ref={ref}
- className={cn(toastVariants({ variant }), className)}
- {...props}
- />
- )
-})
-Toast.displayName = ToastPrimitives.Root.displayName
-
-const ToastAction = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Action>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Action
- ref={ref}
- className={cn(
- "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
- className
- )}
- {...props}
- />
-))
-ToastAction.displayName = ToastPrimitives.Action.displayName
-
-const ToastClose = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Close>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Close
- ref={ref}
- className={cn(
- "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
- className
- )}
- toast-close=""
- {...props}
- >
- <X className="h-4 w-4" />
- </ToastPrimitives.Close>
-))
-ToastClose.displayName = ToastPrimitives.Close.displayName
-
-const ToastTitle = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Title>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Title
- ref={ref}
- className={cn("text-sm font-semibold", className)}
- {...props}
- />
-))
-ToastTitle.displayName = ToastPrimitives.Title.displayName
-
-const ToastDescription = React.forwardRef<
- React.ElementRef<typeof ToastPrimitives.Description>,
- React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
->(({ className, ...props }, ref) => (
- <ToastPrimitives.Description
- ref={ref}
- className={cn("text-sm opacity-90", className)}
- {...props}
- />
-))
-ToastDescription.displayName = ToastPrimitives.Description.displayName
-
-type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
-
-type ToastActionElement = React.ReactElement<typeof ToastAction>
-
-export {
- type ToastProps,
- type ToastActionElement,
- ToastProvider,
- ToastViewport,
- Toast,
- ToastTitle,
- ToastDescription,
- ToastClose,
- ToastAction,
-}
diff --git a/ui/src/components/ui/toaster.tsx b/ui/src/components/ui/toaster.tsx
deleted file mode 100644
index a2209ba5..00000000
--- a/ui/src/components/ui/toaster.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import {
- Toast,
- ToastClose,
- ToastDescription,
- ToastProvider,
- ToastTitle,
- ToastViewport,
-} from "@/components/ui/toast"
-import { useToast } from "@/components/ui/use-toast"
-
-export function Toaster() {
- const { toasts } = useToast()
-
- return (
- <ToastProvider>
- {toasts.map(function ({ id, title, description, action, ...props }) {
- return (
- <Toast key={id} {...props}>
- <div className="grid gap-1">
- {title && <ToastTitle>{title}</ToastTitle>}
- {description && (
- <ToastDescription>{description}</ToastDescription>
- )}
- </div>
- {action}
- <ToastClose />
- </Toast>
- )
- })}
- <ToastViewport />
- </ToastProvider>
- )
-}
diff --git a/ui/src/components/ui/use-toast.ts b/ui/src/components/ui/use-toast.ts
deleted file mode 100644
index 16713070..00000000
--- a/ui/src/components/ui/use-toast.ts
+++ /dev/null
@@ -1,192 +0,0 @@
-// Inspired by react-hot-toast library
-import * as React from "react"
-
-import type {
- ToastActionElement,
- ToastProps,
-} from "@/components/ui/toast"
-
-const TOAST_LIMIT = 1
-const TOAST_REMOVE_DELAY = 1000000
-
-type ToasterToast = ToastProps & {
- id: string
- title?: React.ReactNode
- description?: React.ReactNode
- action?: ToastActionElement
-}
-
-const actionTypes = {
- ADD_TOAST: "ADD_TOAST",
- UPDATE_TOAST: "UPDATE_TOAST",
- DISMISS_TOAST: "DISMISS_TOAST",
- REMOVE_TOAST: "REMOVE_TOAST",
-} as const
-
-let count = 0
-
-function genId() {
- count = (count + 1) % Number.MAX_SAFE_INTEGER
- return count.toString()
-}
-
-type ActionType = typeof actionTypes
-
-type Action =
- | {
- type: ActionType["ADD_TOAST"]
- toast: ToasterToast
- }
- | {
- type: ActionType["UPDATE_TOAST"]
- toast: Partial<ToasterToast>
- }
- | {
- type: ActionType["DISMISS_TOAST"]
- toastId?: ToasterToast["id"]
- }
- | {
- type: ActionType["REMOVE_TOAST"]
- toastId?: ToasterToast["id"]
- }
-
-interface State {
- toasts: ToasterToast[]
-}
-
-const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
-
-const addToRemoveQueue = (toastId: string) => {
- if (toastTimeouts.has(toastId)) {
- return
- }
-
- const timeout = setTimeout(() => {
- toastTimeouts.delete(toastId)
- dispatch({
- type: "REMOVE_TOAST",
- toastId: toastId,
- })
- }, TOAST_REMOVE_DELAY)
-
- toastTimeouts.set(toastId, timeout)
-}
-
-export const reducer = (state: State, action: Action): State => {
- switch (action.type) {
- case "ADD_TOAST":
- return {
- ...state,
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
- }
-
- case "UPDATE_TOAST":
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === action.toast.id ? { ...t, ...action.toast } : t
- ),
- }
-
- case "DISMISS_TOAST": {
- const { toastId } = action
-
- // ! Side effects ! - This could be extracted into a dismissToast() action,
- // but I'll keep it here for simplicity
- if (toastId) {
- addToRemoveQueue(toastId)
- } else {
- state.toasts.forEach((toast) => {
- addToRemoveQueue(toast.id)
- })
- }
-
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === toastId || toastId === undefined
- ? {
- ...t,
- open: false,
- }
- : t
- ),
- }
- }
- case "REMOVE_TOAST":
- if (action.toastId === undefined) {
- return {
- ...state,
- toasts: [],
- }
- }
- return {
- ...state,
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
- }
- }
-}
-
-const listeners: Array<(state: State) => void> = []
-
-let memoryState: State = { toasts: [] }
-
-function dispatch(action: Action) {
- memoryState = reducer(memoryState, action)
- listeners.forEach((listener) => {
- listener(memoryState)
- })
-}
-
-type Toast = Omit<ToasterToast, "id">
-
-function toast({ ...props }: Toast) {
- const id = genId()
-
- const update = (props: ToasterToast) =>
- dispatch({
- type: "UPDATE_TOAST",
- toast: { ...props, id },
- })
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
-
- dispatch({
- type: "ADD_TOAST",
- toast: {
- ...props,
- id,
- open: true,
- onOpenChange: (open) => {
- if (!open) dismiss()
- },
- },
- })
-
- return {
- id: id,
- dismiss,
- update,
- }
-}
-
-function useToast() {
- const [state, setState] = React.useState<State>(memoryState)
-
- React.useEffect(() => {
- listeners.push(setState)
- return () => {
- const index = listeners.indexOf(setState)
- if (index > -1) {
- listeners.splice(index, 1)
- }
- }
- }, [state])
-
- return {
- ...state,
- toast,
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
- }
-}
-
-export { useToast, toast }
diff --git a/ui/src/global.d.ts b/ui/src/global.d.ts
deleted file mode 100644
index d97caa3a..00000000
--- a/ui/src/global.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-type Option<T> = T | null;
diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts
deleted file mode 100644
index db66b3be..00000000
--- a/ui/src/lib/utils.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import type { ClassValue } from "clsx";
-
-import clsx from "clsx";
-import { extendTailwindMerge } from "tailwind-merge";
-
-const COMMON_UNITS = ["small", "medium", "large"];
-
-/**
- * We need to extend the tailwind merge to include NextUI's custom classes.
- *
- * So we can use classes like `text-small` or `text-default-500` and override them.
- */
-const twMerge = extendTailwindMerge({
- extend: {
- theme: {
- opacity: ["disabled"],
- spacing: ["divider"],
- borderWidth: COMMON_UNITS,
- borderRadius: COMMON_UNITS,
- },
- classGroups: {
- shadow: [{ shadow: COMMON_UNITS }],
- "font-size": [{ text: ["tiny", ...COMMON_UNITS] }],
- "bg-image": ["bg-stripe-gradient"],
- },
- },
-});
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
-
-// edge still uses the old one
-export function getWeekInfo() {
- let locale = new Intl.Locale(navigator.language);
-
- // @ts-ignore
- if (locale.getWeekInfo) {
- // @ts-ignore
- return locale.getWeekInfo();
- // @ts-ignore
- } else if (locale.weekInfo) {
- // @ts-ignore
- return locale.weekInfo;
- }
-
- throw new Error("Could not fetch week info via new or old api");
-}
diff --git a/ui/src/main.tsx b/ui/src/main.tsx
deleted file mode 100644
index 5fddc82f..00000000
--- a/ui/src/main.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom/client";
-import { NextUIProvider } from "@nextui-org/react";
-import App from "./App";
-import "./styles.css";
-
-ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
- <React.StrictMode>
- <NextUIProvider>
- <main className="text-foreground bg-background">
- <div
- data-tauri-drag-region
- className="w-full min-h-8 z-10 border-b-1"
- />
-
- <div className="z-20 ">
- <App />
- </div>
- </main>
- </NextUIProvider>
- </React.StrictMode>,
-);
diff --git a/ui/src/pages/Dotfiles.tsx b/ui/src/pages/Dotfiles.tsx
deleted file mode 100644
index 85f5b0e0..00000000
--- a/ui/src/pages/Dotfiles.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { useState } from "react";
-import Aliases from "@/components/dotfiles/Aliases";
-import Vars from "@/components/dotfiles/Vars";
-
-enum Section {
- Aliases,
- Vars,
- Snippets,
-}
-
-function renderDotfiles(current: Section) {
- switch (current) {
- case Section.Aliases:
- return <Aliases />;
- case Section.Vars:
- return <Vars />;
- case Section.Snippets:
- return <div />;
- }
-}
-
-interface HeaderProps {
- current: Section;
- setCurrent: (section: Section) => void;
-}
-
-interface TabsProps {
- current: Section;
- setCurrent: (section: Section) => void;
-}
-
-function Header({ current, setCurrent }: HeaderProps) {
- return (
- <div className="md:flex md:items-center md:justify-between">
- <div className="min-w-0 flex-1">
- <h2 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
- Dotfiles
- </h2>
- </div>
-
- <Tabs current={current} setCurrent={setCurrent} />
- </div>
- );
-}
-
-function classNames(...classes: any[]) {
- return classes.filter(Boolean).join(" ");
-}
-
-function Tabs({ current, setCurrent }: TabsProps) {
- let tabs = [
- {
- name: "Aliases",
- isCurrent: () => current === Section.Aliases,
- section: Section.Aliases,
- },
- {
- name: "Vars",
- isCurrent: () => current === Section.Vars,
- section: Section.Vars,
- },
- {
- name: "Snippets",
- isCurrent: () => current === Section.Snippets,
- section: Section.Snippets,
- },
- ];
-
- return (
- <div>
- <div>
- <nav className="flex space-x-4" aria-label="Tabs">
- {tabs.map((tab) => (
- <button
- onClick={() => {
- setCurrent(tab.section);
- }}
- key={tab.name}
- className={classNames(
- tab.isCurrent()
- ? "bg-gray-100 text-gray-700"
- : "text-gray-500 hover:text-gray-700",
- "rounded-md px-3 py-2 text-sm font-medium",
- )}
- aria-current={tab.isCurrent() ? "page" : undefined}
- >
- {tab.name}
- </button>
- ))}
- </nav>
- </div>
- </div>
- );
-}
-
-export default function Dotfiles() {
- let [current, setCurrent] = useState(Section.Aliases);
- console.log(current);
-
- return (
- <div className="w-full flex-1 flex-col p-4 overflow-y-auto">
- <div className="p-10">
- <Header current={current} setCurrent={setCurrent} />
- Manage your shell aliases, variables and paths
- {renderDotfiles(current)}
- </div>
- </div>
- );
-}
diff --git a/ui/src/pages/History.tsx b/ui/src/pages/History.tsx
deleted file mode 100644
index 32f5217e..00000000
--- a/ui/src/pages/History.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { useEffect, useState, useRef } from "react";
-import { useVirtualizer } from "@tanstack/react-virtual";
-
-import HistoryList from "@/components/HistoryList.tsx";
-import HistorySearch from "@/components/HistorySearch.tsx";
-
-import { AtuinState, useStore } from "@/state/store";
-
-export default function Search() {
- const history = useStore((state: AtuinState) => state.shellHistory);
- const refreshHistory = useStore(
- (state: AtuinState) => state.refreshShellHistory,
- );
- const historyNextPage = useStore(
- (state: AtuinState) => state.historyNextPage,
- );
-
- let [query, setQuery] = useState("");
-
- useEffect(() => {
- (async () => {
- // nothing rn
- })();
-
- refreshHistory();
- }, []);
-
- const parentRef = useRef<HTMLElement | null>(null);
-
- const rowVirtualizer = useVirtualizer({
- count: history.length,
- getScrollElement: () => parentRef.current,
- estimateSize: () => 90,
- overscan: 5,
- });
-
- useEffect(() => {
- const [lastItem] = rowVirtualizer.getVirtualItems().slice(-1);
-
- if (!lastItem) return; // no undefined plz
- if (lastItem.index < history.length - 1) return; // if we're not at the end yet, bail
-
- // we're at the end! more rows plz!
- historyNextPage(query);
- }, [rowVirtualizer.getVirtualItems()]);
-
- return (
- <>
- <div className="w-full flex-1 flex-col">
- <div className="flex h-16 shrink-0 items-center gap-x-4 border-b border-t border-gray-200 bg-white px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8 history-search">
- <HistorySearch
- query={query}
- setQuery={(q) => {
- setQuery(q);
- refreshHistory(q);
- }}
- refresh={() => {
- refreshHistory(query);
- }}
- />
- </div>
-
- <main className="overflow-y-scroll history-list" ref={parentRef}>
- <HistoryList
- history={history}
- items={rowVirtualizer.getVirtualItems()}
- height={rowVirtualizer.getTotalSize()}
- />
- </main>
- </div>
- </>
- );
-}
diff --git a/ui/src/pages/Home.tsx b/ui/src/pages/Home.tsx
deleted file mode 100644
index 2e93a893..00000000
--- a/ui/src/pages/Home.tsx
+++ /dev/null
@@ -1,295 +0,0 @@
-import React, { useEffect } from "react";
-import { formatRelative } from "date-fns";
-import { Tooltip as ReactTooltip } from "react-tooltip";
-
-import { AtuinState, useStore } from "@/state/store";
-import { useToast } from "@/components/ui/use-toast";
-import { ToastAction } from "@/components/ui/toast";
-import { invoke } from "@tauri-apps/api/core";
-import {
- Card,
- CardHeader,
- CardBody,
- Listbox,
- ListboxItem,
-} from "@nextui-org/react";
-
-import {
- Bar,
- BarChart,
- CartesianGrid,
- LabelList,
- XAxis,
- YAxis,
-} from "recharts";
-import { ChartConfig, ChartContainer } from "@/components/ui/chart";
-
-import { Clock, Terminal } from "lucide-react";
-
-import ActivityCalendar from "react-activity-calendar";
-import HistoryRow from "@/components/history/HistoryRow";
-import { ShellHistory } from "@/state/models";
-
-function StatCard({ name, stat }: any) {
- return (
- <Card shadow="sm">
- <CardHeader>
- <h3 className="uppercase text-gray-500">{name}</h3>
- </CardHeader>
- <CardBody>
- <h2 className="font-bold text-xl">{stat}</h2>
- </CardBody>
- </Card>
- );
-}
-
-function TopChart({ chartData }: any) {
- const chartConfig = {
- command: {
- label: "Command",
- color: "#c4edde",
- },
- } satisfies ChartConfig;
-
- return (
- <ChartContainer config={chartConfig} className="max-h-72">
- <BarChart
- accessibilityLayer
- data={chartData}
- layout="vertical"
- margin={{
- right: 16,
- }}
- >
- <CartesianGrid horizontal={false} />
- <YAxis
- dataKey="command"
- type="category"
- tickLine={false}
- tickMargin={10}
- axisLine={false}
- tickFormatter={(value) => value.slice(0, 3)}
- hide
- />
- <XAxis dataKey="count" type="number" hide />
- <Bar dataKey="count" layout="vertical" fill="#c4edde" radius={4}>
- <LabelList
- dataKey="command"
- position="insideLeft"
- offset={8}
- className="fill-[--color-label]"
- fontSize={12}
- />
- <LabelList
- dataKey="count"
- position="right"
- offset={8}
- className="fill-foreground"
- fontSize={12}
- />
- </Bar>
- </BarChart>
- </ChartContainer>
- );
-}
-
-function Header({ name }: any) {
- let greeting = name && name.length > 0 ? "Hey, " + name + "!" : "Hey!";
-
- return (
- <div className="md:flex md:items-center md:justify-between">
- <div className="flex-1">
- <h2 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
- {greeting}
- </h2>
- <h3 className="text-xl leading-7 text-gray-900 pt-4">
- Welcome to{" "}
- <a
- href="https://atuin.sh"
- target="_blank"
- rel="noopener noreferrer nofollow"
- >
- Atuin
- </a>
- .
- </h3>
- </div>
- </div>
- );
-}
-
-const explicitTheme = {
- light: ["#f0f0f0", "#c4edde", "#7ac7c4", "#f73859", "#384259"],
- dark: ["#f0f0f0", "#c4edde", "#7ac7c4", "#f73859", "#384259"],
-};
-
-export default function Home() {
- const homeInfo = useStore((state: AtuinState) => state.homeInfo);
- const user = useStore((state: AtuinState) => state.user);
- const calendar = useStore((state: AtuinState) => state.calendar);
- const runbooks = useStore((state: AtuinState) => state.runbooks);
- const weekStart = useStore((state: AtuinState) => state.weekStart);
-
- const refreshHomeInfo = useStore(
- (state: AtuinState) => state.refreshHomeInfo,
- );
- const refreshUser = useStore((state: AtuinState) => state.refreshUser);
- const refreshCalendar = useStore(
- (state: AtuinState) => state.refreshCalendar,
- );
- const refreshRunbooks = useStore(
- (state: AtuinState) => state.refreshRunbooks,
- );
-
- const { toast } = useToast();
-
- useEffect(() => {
- refreshHomeInfo();
- refreshUser();
- refreshCalendar();
- refreshRunbooks();
-
- console.log(homeInfo);
-
- let setup = async () => {
- let installed = await invoke("is_cli_installed");
- console.log("CLI installation status:", installed);
-
- if (!installed) {
- toast({
- title: "Atuin CLI",
- description: "CLI not detected - install?",
- action: (
- <ToastAction
- altText="Install"
- onClick={() => {
- let install = async () => {
- toast({
- title: "Atuin CLI",
- description: "Install in progress...",
- });
-
- console.log("Installing CLI...");
- await invoke("install_cli");
-
- console.log("Setting up plugin...");
- await invoke("setup_cli");
-
- toast({
- title: "Atuin CLI",
- description: "Installation complete",
- });
- };
- install();
- }}
- >
- Install
- </ToastAction>
- ),
- });
- }
- };
-
- setup();
- }, []);
-
- if (!homeInfo) {
- return <div>Loading...</div>;
- }
-
- return (
- <div className="w-full flex-1 flex-col p-4 overflow-y-auto">
- <div className="pl-10">
- <Header name={user.username} />
- </div>
- <div className="p-10 grid grid-cols-4 gap-4">
- <StatCard
- name="Last Sync"
- stat={
- (homeInfo.lastSyncTime &&
- formatRelative(homeInfo.lastSyncTime, new Date())) ||
- "Never"
- }
- />
- <StatCard
- name="Total Commands"
- stat={homeInfo.historyCount.toLocaleString()}
- />
- <StatCard
- name="Total Runbooks"
- stat={runbooks.length.toLocaleString()}
- />
- <StatCard
- name="Other Records"
- stat={homeInfo.recordCount - homeInfo.historyCount}
- />
-
- <Card shadow="sm" className="col-span-3">
- <CardHeader>
- <h2 className="uppercase text-gray-500">Activity graph</h2>
- </CardHeader>
- <CardBody>
- <ActivityCalendar
- hideTotalCount
- theme={explicitTheme}
- data={calendar}
- weekStart={weekStart as any}
- renderBlock={(block, activity) =>
- React.cloneElement(block, {
- "data-tooltip-id": "react-tooltip",
- "data-tooltip-html": `${activity.count} commands on ${activity.date}`,
- })
- }
- />
- <ReactTooltip id="react-tooltip" />
- </CardBody>
- </Card>
-
- <Card shadow="sm">
- <CardHeader>
- <h2 className="uppercase text-gray-500">Quick actions </h2>
- </CardHeader>
-
- <CardBody>
- <Listbox variant="flat" aria-label="Quick actions">
- <ListboxItem
- key="new-runbook"
- description="Create an executable runbook"
- startContent={<Terminal />}
- >
- New runbook
- </ListboxItem>
- <ListboxItem
- key="shell-history"
- description="Search and explore shell history"
- startContent={<Clock />}
- >
- Shell History
- </ListboxItem>
- </Listbox>
- </CardBody>
- </Card>
-
- <Card shadow="sm" className="col-span-2">
- <CardHeader>
- <h2 className="uppercase text-gray-500">Recent commands</h2>
- </CardHeader>
- <CardBody>
- {homeInfo.recentCommands?.map((i: ShellHistory) => {
- return <HistoryRow compact h={i} />;
- })}
- </CardBody>
- </Card>
-
- <Card shadow="sm" className="col-span-2">
- <CardHeader>
- <h2 className="uppercase text-gray-500">Top commands</h2>
- </CardHeader>
- <CardBody>
- <TopChart chartData={homeInfo.topCommands} />
- </CardBody>
- </Card>
- </div>
- </div>
- );
-}
diff --git a/ui/src/pages/Runbooks.tsx b/ui/src/pages/Runbooks.tsx
deleted file mode 100644
index a0b844a6..00000000
--- a/ui/src/pages/Runbooks.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import Editor from "@/components/runbooks/editor/Editor";
-import List from "@/components/runbooks/List";
-
-import { useStore } from "@/state/store";
-
-export default function Runbooks() {
- const currentRunbook = useStore((store) => store.currentRunbook);
-
- return (
- <div className="flex w-full !max-w-full flex-row ">
- <List />
- {currentRunbook && (
- <div className="flex w-full">
- <Editor />
- </div>
- )}
-
- {!currentRunbook && (
- <div className="flex align-middle justify-center flex-col h-screen w-full">
- <h1 className="text-center">Select or create a runbook</h1>
- </div>
- )}
- </div>
- );
-}
diff --git a/ui/src/state/client.ts b/ui/src/state/client.ts
deleted file mode 100644
index c46fc4e6..00000000
--- a/ui/src/state/client.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// At some point, I'd like to replace some of the Atuin calls
-// with separate state handling here
-
-import { invoke } from "@tauri-apps/api/core";
-import { Settings } from "@/state/models";
-
-export async function sessionToken(): Promise<String> {
- return await invoke("session");
-}
-
-export async function settings(): Promise<Settings> {
- return await invoke("config");
-}
-
-export async function login(
- username: string,
- password: string,
- key: string,
-): Promise<string> {
- return await invoke("login", { username, password, key });
-}
-
-export async function logout(): Promise<string> {
- return await invoke("logout");
-}
-
-export async function register(
- username: string,
- email: string,
- password: string,
-): Promise<string> {
- return await invoke("register", { username, email, password });
-}
diff --git a/ui/src/state/models.ts b/ui/src/state/models.ts
deleted file mode 100644
index 891f7a55..00000000
--- a/ui/src/state/models.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import { invoke } from "@tauri-apps/api/core";
-import Database from "@tauri-apps/plugin-sql";
-
-export class User {
- username: string | null;
-
- constructor(username: string) {
- this.username = username;
- }
-
- isLoggedIn(): boolean {
- return this.username !== "" && this.username !== null;
- }
-}
-
-export const DefaultUser: User = new User("");
-
-export interface HomeInfo {
- historyCount: number;
- recordCount: number;
- lastSyncTime: Date | null;
- recentCommands: ShellHistory[];
- topCommands: ShellHistory[];
-}
-
-export const DefaultHomeInfo: HomeInfo = {
- historyCount: 0,
- recordCount: 0,
- lastSyncTime: new Date(),
- recentCommands: [],
- topCommands: [],
-};
-
-export class ShellHistory {
- id: string;
- timestamp: number;
- command: string;
- user: string;
- host: string;
- cwd: string;
- duration: number;
- exit: number;
-
- /// Pass a row straight from the database to this
- constructor(
- id: string,
- timestamp: number,
- command: string,
- hostuser: string,
- cwd: string,
- duration: number,
- exit: number,
- ) {
- this.id = id;
- this.timestamp = timestamp;
- this.command = command;
-
- let [host, user] = hostuser.split(":");
- this.user = user;
- this.host = host;
-
- this.cwd = cwd;
- this.duration = duration;
- this.exit = exit;
- }
-}
-
-export interface Alias {
- name: string;
- value: string;
-}
-
-export interface Var {
- name: string;
- value: string;
- export: boolean;
-}
-
-export interface InspectHistory {
- other: ShellHistory[];
-}
-
-// Not yet complete. Not all types are defined here.
-// Gonna hold off until the settings refactoring.
-export interface Settings {
- auto_sync: boolean;
- update_check: boolean;
- sync_address: string;
- sync_frequency: string;
- db_path: string;
- record_store_path: string;
- key_path: string;
- session_path: string;
- shell_up_key_binding: boolean;
- inline_height: number;
- invert: boolean;
- show_preview: boolean;
- max_preview_height: number;
- show_help: boolean;
- show_tabs: boolean;
- word_chars: string;
- scroll_context_lines: number;
- history_format: string;
- prefers_reduced_motion: boolean;
- store_failed: boolean;
- secrets_filter: boolean;
- workspaces: boolean;
- ctrl_n_shortcuts: boolean;
- network_connect_timeout: number;
- network_timeout: number;
- local_timeout: number;
- enter_accept: boolean;
- smart_sort: boolean;
- sync: Sync;
-}
-
-interface Sync {
- records: boolean;
-}
-
-// Define other interfaces (Dialect, Timezone, Style, SearchMode, FilterMode, ExitMode, KeymapMode, CursorStyle, WordJumpMode, RegexSet, Stats) accordingly.
-
-export async function inspectCommandHistory(
- h: ShellHistory,
-): Promise<InspectHistory> {
- const settings: Settings = await invoke("cli_settings");
- const db = await Database.load("sqlite:" + settings.db_path);
-
- let other: any[] = await db.select(
- "select * from history where command=?1 order by timestamp desc",
- [h.command],
- );
- console.log(other);
-
- return {
- other: other.map(
- (i) =>
- new ShellHistory(
- i.id,
- i.timestamp,
- i.command,
- i.hostname,
- i.cwd,
- i.duration,
- i.exit,
- ),
- ),
- };
-}
-
-export async function inspectDirectoryHistory(
- h: ShellHistory,
-): Promise<InspectHistory> {
- const settings: Settings = await invoke("cli_settings");
- const db = await Database.load("sqlite:" + settings.db_path);
-
- let other: any[] = await db.select(
- "select * from history where cwd=?1 order by timestamp desc",
- [h.cwd],
- );
- console.log(other);
-
- return {
- other: other.map(
- (i) =>
- new ShellHistory(
- i.id,
- i.timestamp,
- i.command,
- i.hostname,
- i.cwd,
- i.duration,
- i.exit,
- ),
- ),
- };
-}
diff --git a/ui/src/state/runbooks/runbook.ts b/ui/src/state/runbooks/runbook.ts
deleted file mode 100644
index 8555f4ea..00000000
--- a/ui/src/state/runbooks/runbook.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import Database from "@tauri-apps/plugin-sql";
-import { uuidv7 } from "uuidv7";
-
-export default class Runbook {
- id: string;
- created: Date;
- updated: Date;
-
- private _name: string;
- private _content: string;
-
- set name(value: string) {
- this.updated = new Date();
- this._name = value;
- }
-
- set content(value: string) {
- this.updated = new Date();
- this._content = value;
- }
-
- get content() {
- return this._content;
- }
-
- get name() {
- return this._name;
- }
-
- constructor(
- id: string,
- name: string,
- content: string,
- created: Date,
- updated: Date,
- ) {
- this.id = id;
- this._name = name;
- this._content = content;
- this.created = created;
- this.updated = updated;
- }
-
- /// Create a new Runbook, and automatically generate an ID.
- public static async create(): Promise<Runbook> {
- let now = new Date();
-
- // Initialize with the same value for created/updated, to avoid needing null.
- let runbook = new Runbook(uuidv7(), "", "", now, now);
- await runbook.save();
-
- return runbook;
- }
-
- public static async load(id: String): Promise<Runbook | null> {
- const db = await Database.load("sqlite:runbooks.db");
-
- let res = await db.select<any[]>("select * from runbooks where id = $1", [
- id,
- ]);
-
- if (res.length == 0) return null;
-
- let rb = res[0];
-
- return new Runbook(
- rb.id,
- rb.name,
- rb.content,
- new Date(rb.created / 1000000),
- new Date(rb.updated / 1000000),
- );
- }
-
- static async all(): Promise<Runbook[]> {
- const db = await Database.load("sqlite:runbooks.db");
-
- let res = await db.select<any[]>(
- "select * from runbooks order by updated desc",
- );
-
- return res.map((i) => {
- return new Runbook(
- i.id,
- i.name,
- i.content,
- new Date(i.created / 1000000),
- new Date(i.updated / 1000000),
- );
- });
- }
-
- public async save() {
- const db = await Database.load("sqlite:runbooks.db");
-
- await db.execute(
- `insert into runbooks(id, name, content, created, updated)
- values ($1, $2, $3, $4, $5)
-
- on conflict(id) do update
- set
- name=$2,
- content=$3,
- updated=$5`,
-
- // getTime returns a timestamp as unix milliseconds
- // we won't need or use the resolution here, but elsewhere Atuin stores timestamps in sqlite as nanoseconds since epoch
- // let's do that across the board to avoid mistakes
- [
- this.id,
- this._name,
- this._content,
- this.created.getTime() * 1000000,
- this.updated.getTime() * 1000000,
- ],
- );
- }
-
- public static async delete(id: string) {
- const db = await Database.load("sqlite:runbooks.db");
-
- await db.execute("delete from runbooks where id=$1", [id]);
- }
-}
diff --git a/ui/src/state/store.ts b/ui/src/state/store.ts
deleted file mode 100644
index 39ee0096..00000000
--- a/ui/src/state/store.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-import { create } from "zustand";
-import { persist } from "zustand/middleware";
-
-import { parseISO } from "date-fns";
-
-import { fetch } from "@tauri-apps/plugin-http";
-
-import {
- User,
- DefaultUser,
- HomeInfo,
- DefaultHomeInfo,
- Alias,
- ShellHistory,
- Var,
-} from "./models";
-
-import { invoke } from "@tauri-apps/api/core";
-import { sessionToken, settings } from "./client";
-import { getWeekInfo } from "@/lib/utils";
-import Runbook from "./runbooks/runbook";
-import { Terminal } from "@xterm/xterm";
-import { FitAddon } from "@xterm/addon-fit";
-import { WebglAddon } from "@xterm/addon-webgl";
-
-export class TerminalData {
- terminal: Terminal;
- fitAddon: FitAddon;
-
- constructor(terminal: Terminal, fit: FitAddon) {
- this.terminal = terminal;
- this.fitAddon = fit;
- }
-}
-
-// I'll probs want to slice this up at some point, but for now a
-// big blobby lump of state is fine.
-// Totally just hoping that structure will be emergent in the future.
-export interface AtuinState {
- user: User;
- homeInfo: HomeInfo;
- aliases: Alias[];
- vars: Var[];
- shellHistory: ShellHistory[];
- calendar: any[];
- weekStart: number;
- runbooks: Runbook[];
- currentRunbook: string | null;
-
- refreshHomeInfo: () => void;
- refreshCalendar: () => void;
- refreshAliases: () => void;
- refreshVars: () => void;
- refreshUser: () => void;
- refreshRunbooks: () => void;
- refreshShellHistory: (query?: string) => void;
- historyNextPage: (query?: string) => void;
-
- setCurrentRunbook: (id: String) => void;
- setPtyTerm: (pty: string, terminal: any) => void;
- newPtyTerm: (pty: string) => TerminalData;
- cleanupPtyTerm: (pty: string) => void;
-
- terminals: { [pty: string]: TerminalData };
-
- // Store ephemeral state for runbooks, that is not persisted to the database
- runbookInfo: { [runbook: string]: { ptys: number } };
- incRunbookPty: (runbook: string) => void;
- decRunbookPty: (runbook: string) => void;
-}
-
-let state = (set: any, get: any): AtuinState => ({
- user: DefaultUser,
- homeInfo: DefaultHomeInfo,
- aliases: [],
- vars: [],
- shellHistory: [],
- calendar: [],
- runbooks: [],
- currentRunbook: "",
- terminals: {},
- runbookInfo: {},
-
- weekStart: getWeekInfo().firstDay,
-
- refreshAliases: () => {
- invoke("aliases").then((aliases: any) => {
- set({ aliases: aliases });
- });
- },
-
- refreshCalendar: () => {
- invoke("history_calendar").then((calendar: any) => {
- set({ calendar: calendar });
- });
- },
-
- refreshVars: () => {
- invoke("vars").then((vars: any) => {
- set({ vars: vars });
- });
- },
-
- refreshRunbooks: async () => {
- let runbooks = await Runbook.all();
- set({ runbooks });
- },
-
- refreshShellHistory: (query?: string) => {
- if (query) {
- invoke("search", { query: query })
- .then((res: any) => {
- set({ shellHistory: res });
- })
- .catch((e) => {
- console.log(e);
- });
- } else {
- invoke("list").then((res: any) => {
- set({ shellHistory: res });
- });
- }
- },
-
- refreshHomeInfo: () => {
- invoke("home_info")
- .then((res: any) => {
- console.log(res);
- set({
- homeInfo: {
- historyCount: res.history_count,
- recordCount: res.record_count,
- lastSyncTime: (res.last_sync && parseISO(res.last_sync)) || null,
- recentCommands: res.recent_commands,
- topCommands: res.top_commands.map((top: any) => ({
- command: top[0],
- count: top[1],
- })),
- },
- });
- })
- .catch((e) => {
- console.log(e);
- });
- },
-
- refreshUser: async () => {
- let config = await settings();
- let session;
-
- try {
- session = await sessionToken();
- } catch (e) {
- console.log("Not logged in, so not refreshing user");
- set({ user: DefaultUser });
- return;
- }
- let url = config.sync_address + "/api/v0/me";
-
- let res = await fetch(url, {
- headers: {
- Authorization: `Token ${session}`,
- },
- });
- let me = await res.json();
-
- set({ user: new User(me.username) });
- },
-
- historyNextPage: (query?: string) => {
- let history = get().shellHistory;
- let offset = history.length - 1;
-
- if (query) {
- invoke("search", { query: query, offset: offset })
- .then((res: any) => {
- set({ shellHistory: [...history, ...res] });
- })
- .catch((e) => {
- console.log(e);
- });
- } else {
- invoke("list", { offset: offset }).then((res: any) => {
- set({ shellHistory: [...history, ...res] });
- });
- }
- },
-
- setCurrentRunbook: (id: String) => {
- set({ currentRunbook: id });
- },
-
- setPtyTerm: (pty: string, terminal: TerminalData) => {
- set({
- terminals: { ...get().terminals, [pty]: terminal },
- });
- },
-
- cleanupPtyTerm: (pty: string) => {
- set((state: AtuinState) => {
- const terminals = Object.keys(state.terminals).reduce(
- (terms: { [pty: string]: TerminalData }, key) => {
- if (key !== pty) {
- terms[key] = state.terminals[key];
- }
- return terms;
- },
- {},
- );
-
- return { terminals };
- });
- },
-
- newPtyTerm: (pty: string) => {
- let terminal = new Terminal();
-
- // TODO: fallback to canvas, also some sort of setting to allow disabling webgl usage
- // probs fine for now though, it's widely supported. maybe issues on linux.
- terminal.loadAddon(new WebglAddon());
-
- let fitAddon = new FitAddon();
- terminal.loadAddon(fitAddon);
-
- const onResize = (size: { cols: number; rows: number }) => {
- invoke("pty_resize", {
- pid: pty,
- cols: size.cols,
- rows: size.rows,
- });
- };
-
- terminal.onResize(onResize);
-
- let td = new TerminalData(terminal, fitAddon);
-
- set({
- terminals: { ...get().terminals, [pty]: td },
- });
-
- return td;
- },
-
- incRunbookPty: (runbook: string) => {
- set((state: AtuinState) => {
- let oldVal = state.runbookInfo[runbook] || { ptys: 0 };
- let newVal = { ptys: oldVal.ptys + 1 };
- console.log(newVal);
-
- return {
- runbookInfo: {
- ...state.runbookInfo,
- [runbook]: newVal,
- },
- };
- });
- },
-
- decRunbookPty: (runbook: string) => {
- set((state: AtuinState) => {
- let newVal = state.runbookInfo[runbook];
- if (!newVal) {
- return;
- }
-
- newVal.ptys--;
-
- return {
- runbookInfo: {
- ...state.runbookInfo,
- [runbook]: newVal,
- },
- };
- });
- },
-});
-
-export const useStore = create<AtuinState>()(
- persist(state, {
- name: "atuin-storage",
-
- // don't serialize the terminals map
- // it won't work as JSON. too cyclical
- partialize: (state) =>
- Object.fromEntries(
- Object.entries(state).filter(([key]) => !["terminals"].includes(key)),
- ),
- }),
-);
diff --git a/ui/src/styles.css b/ui/src/styles.css
deleted file mode 100644
index 27e12ec6..00000000
--- a/ui/src/styles.css
+++ /dev/null
@@ -1,76 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- :root {
- --background: 0 0% 100%;
- --foreground: 222.2 84% 4.9%;
-
- --card: 0 0% 100%;
- --card-foreground: 222.2 84% 4.9%;
-
- --popover: 0 0% 100%;
- --popover-foreground: 222.2 84% 4.9%;
-
- --primary: 222.2 47.4% 11.2%;
- --primary-foreground: 210 40% 98%;
-
- --secondary: 210 40% 96.1%;
- --secondary-foreground: 222.2 47.4% 11.2%;
-
- --muted: 210 40% 96.1%;
- --muted-foreground: 215.4 16.3% 46.9%;
-
- --accent: 210 40% 96.1%;
- --accent-foreground: 222.2 47.4% 11.2%;
-
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 210 40% 98%;
-
- --border: 214.3 31.8% 91.4%;
- --input: 214.3 31.8% 91.4%;
- --ring: 222.2 84% 4.9%;
-
- --radius: 0.5rem;
- }
-
- .dark {
- --background: 222.2 84% 4.9%;
- --foreground: 210 40% 98%;
-
- --card: 222.2 84% 4.9%;
- --card-foreground: 210 40% 98%;
-
- --popover: 222.2 84% 4.9%;
- --popover-foreground: 210 40% 98%;
-
- --primary: 210 40% 98%;
- --primary-foreground: 222.2 47.4% 11.2%;
-
- --secondary: 217.2 32.6% 17.5%;
- --secondary-foreground: 210 40% 98%;
-
- --muted: 217.2 32.6% 17.5%;
- --muted-foreground: 215 20.2% 65.1%;
-
- --accent: 217.2 32.6% 17.5%;
- --accent-foreground: 210 40% 98%;
-
- --destructive: 0 62.8% 30.6%;
- --destructive-foreground: 210 40% 98%;
-
- --border: 217.2 32.6% 17.5%;
- --input: 217.2 32.6% 17.5%;
- --ring: 212.7 26.8% 83.9%;
- }
-}
-
-@layer base {
- * {
- @apply border-border;
- }
- body {
- @apply bg-background text-foreground;
- }
-}
diff --git a/ui/src/vite-env.d.ts b/ui/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe2..00000000
--- a/ui/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-/// <reference types="vite/client" />