aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/components/dotfiles/Vars.tsx194
-rw-r--r--ui/src/pages/Dotfiles.tsx90
-rw-r--r--ui/src/state/models.ts6
-rw-r--r--ui/src/state/store.ts9
4 files changed, 296 insertions, 3 deletions
diff --git a/ui/src/components/dotfiles/Vars.tsx b/ui/src/components/dotfiles/Vars.tsx
new file mode 100644
index 00000000..00317b23
--- /dev/null
+++ b/ui/src/components/dotfiles/Vars.tsx
@@ -0,0 +1,194 @@
+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(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}
+ 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/pages/Dotfiles.tsx b/ui/src/pages/Dotfiles.tsx
index 6b0870b3..29b6b54a 100644
--- a/ui/src/pages/Dotfiles.tsx
+++ b/ui/src/pages/Dotfiles.tsx
@@ -1,6 +1,35 @@
+import { useState } from "react";
import Aliases from "@/components/dotfiles/Aliases";
+import Vars from "@/components/dotfiles/Vars";
-function Header() {
+enum Section {
+ Aliases,
+ Vars,
+ Scripts,
+}
+
+function renderDotfiles(current: Section) {
+ switch (current) {
+ case Section.Aliases:
+ return <Aliases />;
+ case Section.Vars:
+ return <Vars />;
+ case Section.Scripts:
+ 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">
@@ -8,17 +37,72 @@ function Header() {
Dotfiles
</h2>
</div>
+
+ <Tabs current={current} setCurrent={setCurrent} />
+ </div>
+ );
+}
+
+function classNames(...classes) {
+ 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: "Scripts",
+ isCurrent: () => current === Section.Scripts,
+ section: Section.Scripts,
+ },
+ ];
+
+ return (
+ <div>
+ <div className="mt-4">
+ <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="pl-60">
<div className="p-10">
- <Header />
+ <Header current={current} setCurrent={setCurrent} />
Manage your shell aliases, variables and paths
- <Aliases />
+ {renderDotfiles(current)}
</div>
</div>
);
diff --git a/ui/src/state/models.ts b/ui/src/state/models.ts
index f11ce651..5afcb804 100644
--- a/ui/src/state/models.ts
+++ b/ui/src/state/models.ts
@@ -32,3 +32,9 @@ export interface Alias {
name: string;
value: string;
}
+
+export interface Var {
+ name: string;
+ value: string;
+ export: bool;
+}
diff --git a/ui/src/state/store.ts b/ui/src/state/store.ts
index 08410ba8..7e237d70 100644
--- a/ui/src/state/store.ts
+++ b/ui/src/state/store.ts
@@ -19,10 +19,12 @@ interface AtuinState {
user: User;
homeInfo: HomeInfo;
aliases: Alias[];
+ vars: Var[];
shellHistory: ShellHistory[];
refreshHomeInfo: () => void;
refreshAliases: () => void;
+ refreshVars: () => void;
refreshShellHistory: (query?: string) => void;
}
@@ -30,6 +32,7 @@ export const useStore = create<AtuinState>()((set) => ({
user: DefaultUser,
homeInfo: DefaultHomeInfo,
aliases: [],
+ vars: [],
shellHistory: [],
refreshAliases: () => {
@@ -38,6 +41,12 @@ export const useStore = create<AtuinState>()((set) => ({
});
},
+ refreshVars: () => {
+ invoke("vars").then((vars: any) => {
+ set({ vars: vars });
+ });
+ },
+
refreshShellHistory: (query?: string) => {
if (query) {
invoke("search", { query: query })