UNPKG

@buildappolis/env-manager

Version:

Enterprise-grade environment variable management system with encryption, snapshots, and validation

1,308 lines (1,292 loc) 107 kB
import { c as createComponent, a as createAstro, d as renderComponent, r as renderTemplate, m as maybeRenderHead } from '../chunks/astro/server_DE_7F_eO.mjs'; import { $ as $$Layout8Bit, V as VersionFooter } from '../chunks/VersionFooter_B4W5GPCR.mjs'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { useState, useEffect, useRef } from 'react'; import { cva } from 'class-variance-authority'; import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import { Slot } from '@radix-ui/react-slot'; /* empty css */ import * as LabelPrimitive from '@radix-ui/react-label'; import * as SelectPrimitive from '@radix-ui/react-select'; import { ChevronDownIcon, CheckIcon, ChevronUpIcon, XIcon, History, ChevronDown, ChevronRight, GitBranch, Clock, User, Package, FileText, FileEdit, Plus, X, GitCommit, Save, AlertTriangle, Edit3, EyeOff, Eye, Trash2, Download, Upload, FileDown, Check, Copy, Info, FileUp, AlertCircle, Unlock, FolderOpen, Database, Key, Shield, Terminal, FileInput, FileOutput, RefreshCw, Lock } from 'lucide-react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; /* empty css */ import fs from 'fs'; import path from 'path'; import os from 'os'; export { renderers } from '../renderers.mjs'; function cn(...inputs) { return twMerge(clsx(inputs)); } const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", { variants: { variant: { default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline" }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", lg: "h-10 rounded-md px-6 has-[>svg]:px-4", icon: "size-9" } }, defaultVariants: { variant: "default", size: "default" } } ); function Button$1({ className, variant, size, asChild = false, ...props }) { const Comp = asChild ? Slot : "button"; return /* @__PURE__ */ jsx( Comp, { "data-slot": "button", className: cn(buttonVariants({ variant, size, className })), ...props } ); } cva("", { variants: { font: { normal: "", retro: "retro" }, variant: { default: "bg-foreground", destructive: "bg-foreground", outline: "bg-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-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", lg: "h-10 rounded-md px-6 has-[>svg]:px-4", icon: "size-9" } }, defaultVariants: { variant: "default", size: "default" } }); function Button({ children, asChild, ...props }) { const { variant, size, className, font } = props; return /* @__PURE__ */ jsx( Button$1, { ...props, className: cn( "rounded-none active:translate-y-1 transition-transform relative inline-flex items-center justify-center gap-1.5", font !== "normal" && "retro", className ), size, variant, asChild, children: asChild ? /* @__PURE__ */ jsxs("span", { className: "relative inline-flex items-center justify-center gap-1.5", children: [ children, variant !== "ghost" && variant !== "link" && size !== "icon" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "absolute -top-1.5 w-1/2 left-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute -top-1.5 w-1/2 right-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 w-1/2 left-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 w-1/2 right-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-0 right-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 right-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 -left-1.5 h-2/3 w-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 -right-1.5 h-2/3 w-1.5 bg-foreground dark:bg-ring" }), variant !== "outline" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 w-full h-1.5 bg-foreground/20" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 left-0 w-3 h-1.5 bg-foreground/20" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 w-full h-1.5 bg-foreground/20" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-1.5 right-0 w-3 h-1.5 bg-foreground/20" }) ] }) ] }), size === "icon" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 w-full h-[5px] md:h-1.5 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 w-full h-[5px] md:h-1.5 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1 -left-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-1 -left-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1 -right-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-1 -right-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }) ] }) ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [ children, variant !== "ghost" && variant !== "link" && size !== "icon" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "absolute -top-1.5 w-1/2 left-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute -top-1.5 w-1/2 right-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 w-1/2 left-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 w-1/2 right-1.5 h-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-0 right-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 right-0 size-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 -left-1.5 h-2/3 w-1.5 bg-foreground dark:bg-ring" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 -right-1.5 h-2/3 w-1.5 bg-foreground dark:bg-ring" }), variant !== "outline" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 w-full h-1.5 bg-foreground/20" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 left-0 w-3 h-1.5 bg-foreground/20" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 w-full h-1.5 bg-foreground/20" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-1.5 right-0 w-3 h-1.5 bg-foreground/20" }) ] }) ] }), size === "icon" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 w-full h-[5px] md:h-1.5 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 w-full h-[5px] md:h-1.5 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1 -left-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-1 -left-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute top-1 -right-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }), /* @__PURE__ */ jsx("div", { className: "absolute bottom-1 -right-1 w-[5px] md:w-1.5 h-1/2 bg-foreground dark:bg-ring pointer-events-none" }) ] }) ] }) } ); } function Card$1({ className, ...props }) { return /* @__PURE__ */ jsx( "div", { "data-slot": "card", className: cn( "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className ), ...props } ); } function CardHeader$1({ className, ...props }) { return /* @__PURE__ */ jsx( "div", { "data-slot": "card-header", className: cn( "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className ), ...props } ); } function CardTitle$1({ className, ...props }) { return /* @__PURE__ */ jsx( "div", { "data-slot": "card-title", className: cn("leading-none font-semibold", className), ...props } ); } function CardDescription$1({ className, ...props }) { return /* @__PURE__ */ jsx( "div", { "data-slot": "card-description", className: cn("text-muted-foreground text-sm", className), ...props } ); } function CardContent$1({ className, ...props }) { return /* @__PURE__ */ jsx( "div", { "data-slot": "card-content", className: cn("px-6", className), ...props } ); } cva("", { variants: { font: { normal: "", retro: "retro" } }, defaultVariants: { font: "retro" } }); function Card({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsxs( "div", { className: cn( "relative border-y-6 border-foreground dark:border-ring !p-0", className ), children: [ /* @__PURE__ */ jsx( Card$1, { ...props, className: cn( "rounded-none border-0 !w-full", font !== "normal" && "retro", className ) } ), /* @__PURE__ */ jsx( "div", { className: "absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none", "aria-hidden": "true" } ) ] } ); } function CardHeader({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsx( CardHeader$1, { className: cn(font !== "normal" && "retro", className), ...props } ); } function CardTitle({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsx( CardTitle$1, { className: cn(font !== "normal" && "retro", className), ...props } ); } function CardDescription({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsx( CardDescription$1, { className: cn(font !== "normal" && "retro", className), ...props } ); } function CardContent({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsx( CardContent$1, { className: cn(font !== "normal" && "retro", className), ...props } ); } function Input$1({ className, type, ...props }) { return /* @__PURE__ */ jsx( "input", { type, "data-slot": "input", className: cn( "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className ), ...props } ); } cva("", { variants: { font: { normal: "", retro: "retro" } }, defaultVariants: { font: "retro" } }); function Input({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsxs( "div", { className: cn( "relative border-y-6 border-foreground dark:border-ring !p-0 flex items-center", className ), children: [ /* @__PURE__ */ jsx( Input$1, { ...props, className: cn( "rounded-none ring-0 !w-full", font !== "normal" && "retro", className ) } ), /* @__PURE__ */ jsx( "div", { className: "absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none", "aria-hidden": "true" } ) ] } ); } function Label$1({ className, ...props }) { return /* @__PURE__ */ jsx( LabelPrimitive.Root, { "data-slot": "label", className: cn( "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className ), ...props } ); } cva("", { variants: { font: { normal: "", retro: "retro" } }, defaultVariants: { font: "retro" } }); function Label({ className, font, ...props }) { return /* @__PURE__ */ jsx( Label$1, { className: cn(className, font !== "normal" && "retro"), ...props } ); } function Select$1({ ...props }) { return /* @__PURE__ */ jsx(SelectPrimitive.Root, { "data-slot": "select", ...props }); } function SelectValue$1({ ...props }) { return /* @__PURE__ */ jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props }); } function SelectTrigger$1({ className, size = "default", children, ...props }) { return /* @__PURE__ */ jsxs( SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn( "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className ), ...props, children: [ children, /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) }) ] } ); } function SelectContent$1({ className, children, position = "popper", ...props }) { return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs( SelectPrimitive.Content, { "data-slot": "select-content", className: cn( "bg-popover text-popover-foreground 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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className ), position, ...props, children: [ /* @__PURE__ */ jsx(SelectScrollUpButton, {}), /* @__PURE__ */ jsx( SelectPrimitive.Viewport, { className: cn( "p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1" ), children } ), /* @__PURE__ */ jsx(SelectScrollDownButton, {}) ] } ) }); } function SelectItem$1({ className, children, ...props }) { return /* @__PURE__ */ jsxs( SelectPrimitive.Item, { "data-slot": "select-item", className: cn( "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className ), ...props, children: [ /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-4" }) }) }), /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }) ] } ); } function SelectScrollUpButton({ className, ...props }) { return /* @__PURE__ */ jsx( SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn( "flex cursor-default items-center justify-center py-1", className ), ...props, children: /* @__PURE__ */ jsx(ChevronUpIcon, { className: "size-4" }) } ); } function SelectScrollDownButton({ className, ...props }) { return /* @__PURE__ */ jsx( SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn( "flex cursor-default items-center justify-center py-1", className ), ...props, children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4" }) } ); } cva("", { variants: { font: { normal: "", retro: "retro" } }, defaultVariants: { font: "retro" } }); function Select({ ...props }) { return /* @__PURE__ */ jsx(Select$1, { ...props }); } function SelectValue({ ...props }) { const { font } = props; return /* @__PURE__ */ jsx( SelectValue$1, { className: cn(font !== "normal" && "retro"), ...props } ); } function SelectTrigger({ children, ...props }) { const { className, font } = props; return /* @__PURE__ */ jsxs( "div", { className: cn( "relative border-y-6 border-foreground dark:border-ring", className, font !== "normal" && "retro" ), children: [ /* @__PURE__ */ jsx( SelectTrigger$1, { ...props, className: cn("rounded-none ring-0 w-full border-0", className), children } ), /* @__PURE__ */ jsx( "div", { className: "absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none", "aria-hidden": "true" } ) ] } ); } function SelectContent({ className, children, ...props }) { const { font } = props; return /* @__PURE__ */ jsx( SelectContent$1, { className: cn( font !== "normal" && "retro", className, "relative rounded-none border-4 border-foreground dark:border-ring -ml-1 mt-1" ), ...props, children } ); } function SelectItem({ className, children, ...props }) { return /* @__PURE__ */ jsx( SelectItem$1, { className: cn( className, "rounded-none border-y-3 border-dashed border-ring/0 hover:border-foreground dark:hover:border-ring" ), ...props, children } ); } function MissingVariableItem({ variable, groupKey, currentProject, selectedBranch, onSuccess, playSound }) { const [tempValue, setTempValue] = useState(""); const [isAdding, setIsAdding] = useState(false); const isSensitive = variable.sensitive ?? true; const handleAdd = async () => { setIsAdding(true); try { const res = await fetch(`/api/variables?projectPath=${encodeURIComponent(currentProject?.path || "")}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: variable.name, value: tempValue, description: variable.description || "", sensitive: isSensitive, category: groupKey, branch: selectedBranch }) }); if (res.ok) { onSuccess(); setTempValue(""); playSound("powerup"); } else { playSound("error"); } } catch (err) { console.error("Failed to add variable:", err); playSound("error"); } setIsAdding(false); }; return /* @__PURE__ */ jsxs("div", { className: "p-3 bg-red-900/20 border border-red-500/50 rounded", children: [ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx("p", { className: "font-mono text-sm text-red-400", children: variable.name }), /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [ variable.type && /* @__PURE__ */ jsx("span", { className: "px-1 py-0.5 text-xs bg-blue-500/20 text-blue-400 rounded", children: variable.type.toUpperCase() }), variable.sensitive && /* @__PURE__ */ jsx("span", { className: "px-1 py-0.5 text-xs bg-yellow-500/20 text-yellow-400 rounded", children: "SENSITIVE" }) ] }) ] }) }), variable.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 mb-2", children: variable.description }), /* @__PURE__ */ jsxs("form", { onSubmit: (e) => { e.preventDefault(); handleAdd(); }, className: "space-y-2", children: [ /* @__PURE__ */ jsx( "input", { type: "text", name: "username", value: variable.name, autoComplete: "username", style: { display: "none" }, readOnly: true, "aria-hidden": "true" } ), /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [ /* @__PURE__ */ jsx( Input, { type: isSensitive ? "password" : "text", value: tempValue, onChange: (e) => setTempValue(e.target.value), placeholder: "Enter value...", className: "flex-1 text-sm", disabled: isAdding, autoComplete: isSensitive ? "new-password" : "off" } ), /* @__PURE__ */ jsx( Button, { type: "submit", size: "sm", variant: "default", disabled: !tempValue || isAdding, children: isAdding ? "ADDING..." : "ADD" } ) ] }), isSensitive && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 text-xs text-yellow-400", children: /* @__PURE__ */ jsx("span", { children: "⚠️ This value will be encrypted" }) }) ] }) ] }); } function Dialog$1({ ...props }) { return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props }); } function DialogTrigger$1({ ...props }) { return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props }); } function DialogPortal({ ...props }) { return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props }); } function DialogOverlay({ className, ...props }) { return /* @__PURE__ */ jsx( DialogPrimitive.Overlay, { "data-slot": "dialog-overlay", className: cn( "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", className ), ...props } ); } function DialogContent$1({ className, children, showCloseButton = true, ...props }) { return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [ /* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs( DialogPrimitive.Content, { "data-slot": "dialog-content", className: cn( "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg", className ), ...props, children: [ children, showCloseButton && /* @__PURE__ */ jsxs( DialogPrimitive.Close, { "data-slot": "dialog-close", className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [ /* @__PURE__ */ jsx(XIcon, {}), /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" }) ] } ) ] } ) ] }); } function DialogHeader$1({ className, ...props }) { return /* @__PURE__ */ jsx( "div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2 text-center sm:text-left", className), ...props } ); } function DialogTitle$1({ className, ...props }) { return /* @__PURE__ */ jsx( DialogPrimitive.Title, { "data-slot": "dialog-title", className: cn("text-lg leading-none font-semibold", className), ...props } ); } function DialogDescription$1({ className, ...props }) { return /* @__PURE__ */ jsx( DialogPrimitive.Description, { "data-slot": "dialog-description", className: cn("text-muted-foreground text-sm", className), ...props } ); } const Dialog = Dialog$1; const DialogTrigger = DialogTrigger$1; const DialogHeader = DialogHeader$1; const DialogDescription = DialogDescription$1; function DialogTitle({ ...props }) { const { className, font } = props; return /* @__PURE__ */ jsx( DialogTitle$1, { className: cn(font !== "normal" && "retro", className), ...props } ); } cva("", { variants: { font: { normal: "", retro: "retro" } }, defaultVariants: { font: "retro" } }); function DialogContent({ className, children, font, ...props }) { return /* @__PURE__ */ jsxs( DialogContent$1, { className: cn( "bg-card rounded-none border-none", font !== "normal" && "retro", className ), ...props, children: [ children, /* @__PURE__ */ jsx( "div", { className: "absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none", "aria-hidden": "true" } ), /* @__PURE__ */ jsx( "div", { className: "absolute inset-0 border-y-6 -my-1.5 border-foreground dark:border-ring pointer-events-none", "aria-hidden": "true" } ) ] } ); } function VersionHistory({ projectPath, onRestoreVersion, playSound }) { const [versions, setVersions] = useState([]); const [isLoading, setIsLoading] = useState(false); const [selectedVersion, setSelectedVersion] = useState(null); const [expandedVersions, setExpandedVersions] = useState(/* @__PURE__ */ new Set()); useEffect(() => { loadVersionHistory(); }, [projectPath]); const loadVersionHistory = async () => { setIsLoading(true); try { const res = await fetch(`/api/versions${projectPath ? `?projectPath=${encodeURIComponent(projectPath)}` : ""}`); if (res.ok) { const data = await res.json(); setVersions(data.versions || []); } } catch (error) { console.error("Failed to load version history:", error); playSound?.("error"); } setIsLoading(false); }; const handleRestoreVersion = async (versionId) => { if (!confirm("Are you sure you want to restore this version? This will create a new draft with the restored state.")) { return; } try { const res = await fetch("/api/versions/restore", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ versionId, projectPath }) }); if (res.ok) { onRestoreVersion?.(versionId); playSound?.("powerup"); } else { playSound?.("error"); } } catch (error) { console.error("Failed to restore version:", error); playSound?.("error"); } }; const toggleVersionExpanded = (versionId) => { const newExpanded = new Set(expandedVersions); if (newExpanded.has(versionId)) { newExpanded.delete(versionId); } else { newExpanded.add(versionId); } setExpandedVersions(newExpanded); }; const formatChangeType = (type) => { switch (type) { case "create": return { text: "ADDED", color: "text-green-400" }; case "update": return { text: "MODIFIED", color: "text-yellow-400" }; case "delete": return { text: "DELETED", color: "text-red-400" }; case "none": default: return { text: "UNCHANGED", color: "text-gray-400" }; } }; const getTimeDifference = (timestamp) => { const now = /* @__PURE__ */ new Date(); const then = new Date(timestamp); const diffMs = now.getTime() - then.getTime(); const diffMinutes = Math.floor(diffMs / (1e3 * 60)); const diffHours = Math.floor(diffMinutes / 60); const diffDays = Math.floor(diffHours / 24); if (diffMinutes < 1) return "Just now"; if (diffMinutes < 60) return `${diffMinutes}m ago`; if (diffHours < 24) return `${diffHours}h ago`; return `${diffDays}d ago`; }; return /* @__PURE__ */ jsxs(Card, { className: "h-full", children: [ /* @__PURE__ */ jsxs(CardHeader, { children: [ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx(History, { className: "h-5 w-5" }), "VERSION HISTORY" ] }), /* @__PURE__ */ jsx(CardDescription, { children: "Track and manage environment variable changes over time" }) ] }), /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "text-center py-8", children: /* @__PURE__ */ jsx("div", { className: "animate-pulse", children: "Loading version history..." }) }) : versions.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-8 text-gray-400", children: [ /* @__PURE__ */ jsx(History, { className: "h-12 w-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ jsx("p", { children: "No version history available" }), /* @__PURE__ */ jsx("p", { className: "text-sm", children: "Publish some changes to start tracking versions" }) ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-3 max-h-96 overflow-y-auto", children: versions.map((version) => { const isExpanded = expandedVersions.has(version.id); return /* @__PURE__ */ jsxs( "div", { className: "border border-gray-600 rounded-lg p-4 hover:border-gray-500 transition-colors", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "sm", onClick: () => toggleVersionExpanded(version.id), className: "p-1 h-6 w-6", children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) } ), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx(GitBranch, { className: "h-4 w-4 text-blue-400" }), /* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-bold text-blue-400", children: version.version }), version.published && /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs bg-green-500/20 text-green-400 rounded", children: "PUBLISHED" }) ] }), /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mt-1", children: version.description }) ] }) ] }), /* @__PURE__ */ jsxs("div", { className: "text-right text-sm text-gray-400", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }), getTimeDifference(version.timestamp) ] }), version.author && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 mt-1", children: [ /* @__PURE__ */ jsx(User, { className: "h-3 w-3" }), version.author ] }) ] }) ] }), /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [ /* @__PURE__ */ jsx(Package, { className: "h-3 w-3" }), version.variableCount, " variables" ] }), /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [ /* @__PURE__ */ jsx(FileText, { className: "h-3 w-3" }), version.changes.length, " changes" ] }) ] }), /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [ /* @__PURE__ */ jsxs(Dialog, { children: [ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setSelectedVersion(version), children: "VIEW DETAILS" }) }), /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-2xl", children: [ /* @__PURE__ */ jsxs(DialogHeader, { children: [ /* @__PURE__ */ jsxs(DialogTitle, { children: [ "Version ", selectedVersion?.version, " Details" ] }), /* @__PURE__ */ jsx(DialogDescription, { children: selectedVersion?.description }) ] }), /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 text-sm", children: [ /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Published:" }), " ", new Date(selectedVersion?.timestamp || "").toLocaleString() ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Author:" }), " ", selectedVersion?.author || "Unknown" ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Variables:" }), " ", selectedVersion?.variableCount ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Changes:" }), " ", selectedVersion?.changes.length ] }) ] }), selectedVersion && selectedVersion.changes.length > 0 && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("h4", { className: "font-semibold mb-2", children: "Changes:" }), /* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-64 overflow-y-auto", children: selectedVersion.changes.map((change, idx) => { const { text, color } = formatChangeType(change.type); return /* @__PURE__ */ jsxs("div", { className: "border border-gray-600 rounded p-3", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [ /* @__PURE__ */ jsx("span", { className: "font-mono text-sm", children: change.name }), /* @__PURE__ */ jsx("span", { className: `text-xs font-bold ${color}`, children: text }) ] }), change.type !== "create" && change.oldValue && /* @__PURE__ */ jsxs("div", { className: "text-xs text-red-400", children: [ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: "Old:" }), " ", change.sensitive ? "••••••••" : change.oldValue ] }), change.type !== "delete" && change.newValue && /* @__PURE__ */ jsxs("div", { className: "text-xs text-green-400", children: [ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: "New:" }), " ", change.sensitive ? "••••••••" : change.newValue ] }) ] }, idx); }) }) ] }) ] }) ] }) ] }), /* @__PURE__ */ jsx( Button, { variant: "secondary", size: "sm", onClick: () => handleRestoreVersion(version.id), children: "RESTORE" } ) ] }) ] }), isExpanded && /* @__PURE__ */ jsxs("div", { className: "mt-3 pt-3 border-t border-gray-600", children: [ /* @__PURE__ */ jsx("h5", { className: "text-xs font-semibold text-gray-400 mb-2", children: "CHANGES IN THIS VERSION:" }), /* @__PURE__ */ jsx("div", { className: "space-y-1", children: version.changes.map((change, idx) => { const { text, color } = formatChangeType(change.type); return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [ /* @__PURE__ */ jsx("span", { className: "font-mono", children: change.name }), /* @__PURE__ */ jsx("span", { className: `font-bold ${color}`, children: text }) ] }, idx); }) }) ] }) ] }, version.id ); }) }) }) ] }); } function DraftMode({ projectPath, onPublish, onDiscard, playSound }) { const [draft, setDraft] = useState(null); const [draftVariables, setDraftVariables] = useState([]); const [changes, setChanges] = useState([]); const [isLoading, setIsLoading] = useState(false); const [publishDescription, setPublishDescription] = useState(""); const [showSensitive, setShowSensitive] = useState(/* @__PURE__ */ new Set()); const [editingVariable, setEditingVariable] = useState(null); useEffect(() => { loadDraftState(); }, [projectPath]); const loadDraftState = async () => { setIsLoading(true); try { const res = await fetch(`/api/draft${projectPath ? `?projectPath=${encodeURIComponent(projectPath)}` : ""}`); if (res.ok) { const data = await res.json(); setDraft(data.draft); setDraftVariables(data.variables || []); setChanges(data.changes || []); setPublishDescription(data.draft?.description || ""); } } catch (error) { console.error("Failed to load draft state:", error); playSound?.("error"); } setIsLoading(false); }; const createDraft = async () => { try { const res = await fetch("/api/draft", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "create", projectPath, description: publishDescription }) }); if (res.ok) { await loadDraftState(); playSound?.("powerup"); } } catch (error) { console.error("Failed to create draft:", error); playSound?.("error"); } }; const updateDraftVariable = async (name, updates) => { try { const res = await fetch("/api/draft", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "update_variable", projectPath, name, updates }) }); if (res.ok) { await loadDraftState(); setEditingVariable(null); playSound?.("hit"); } } catch (error) { console.error("Failed to update draft variable:", error); playSound?.("error"); } }; const removeFromDraft = async (name) => { try { const res = await fetch("/api/draft", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "remove_variable", projectPath, name }) }); if (res.ok) { await loadDraftState(); playSound?.("hit"); } } catch (error) { console.error("Failed to remove variable from draft:", error); playSound?.("error"); } }; const publishDraft = async () => { if (!draft || changes.length === 0) return; try { const res = await fetch("/api/draft", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "publish", projectPath, description: publishDescription }) }); if (res.ok) { const data = await res.json(); await loadDraftState(); onPublish?.(); playSound?.("success"); setPublishDescription(""); } } catch (error) { console.error("Failed to publish draft:", error); playSound?.("error"); } }; const discardDraft = async () => { if (!confirm("Are you sure you want to discard all draft changes? This cannot be undone.")) { return; } try { const res = await fetch("/api/draft", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "discard", projectPath }) }); if (res.ok) { await loadDraftState(); onDiscard?.(); playSound?.("hit"); setPublishDescription(""); } } catch (error) { console.error("Failed to discard draft:", error); playSound?.("error"); } }; const toggleSensitiveVisibility = (name) => { const newSet = new Set(showSensitive); if (newSet.has(name)) { newSet.delete(name); } else { newSet.add(name); } setShowSensitive(newSet); }; const getChangeTypeDisplay = (type) => { switch (type) { case "create": return { text: "NEW", color: "bg-green-500/20 text-green-400", icon: Plus }; case "update": return { text: "MODIFIED", color: "bg-yellow-500/20 text-yellow-400", icon: Edit3 }; case "delete": return { text: "DELETE", color: "bg-red-500/20 text-red-400", icon: Trash2 }; case "none": default: return { text: "UNCHANGED", color: "bg-gray-500/20 text-gray-400", icon: Edit3 }; } }; if (isLoading) { return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "p-8 text-center", children: /* @__PURE__ */ jsx("div", { className: "animate-pulse", children: "Loading draft state..." }) }) }); } if (!draft) { return /* @__PURE__ */ jsxs(Card, { children: [ /* @__PURE__ */ jsxs(CardHeader, { children: [ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx(FileEdit, { className: "h-5 w-5" }), "DRAFT MODE" ] }), /* @__PURE__ */ jsx(CardDescription, { children: "Start drafting changes to edit multiple variables before publishing" }) ] }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [ /* @__PURE__ */ jsx(FileEdit, { className: "h-12 w-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ jsx("p", { className: "mb-4", children: "No active draft session" }), /* @__PURE__ */ jsxs(Button, { onClick: createDraft, children: [ /* @__PURE__ */ jsx(Plus, { className: "mr-2 h-4 w-4" }), "START DRAFT" ] }) ] }) }) ] }); } return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [ /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardHeader, { children: [ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx(FileEdit, { className: "h-5 w-5" }), "DRAFT MODE", /* @__PURE__ */ jsx("span", { className: "px-2 py-1 text-xs bg-blue-500/20 text-blue-400 rounded", children: "ACTIVE" }) ] }), /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [ /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: discardDraft, children: [ /* @__PURE__ */ jsx(X, { className: "mr-2 h-4 w-4" }), "DISCARD" ] }), /* @__PURE__ */ jsxs(Dialog, { children: [ /* @__PURE__ */ jsx(