@buildappolis/env-manager
Version:
Enterprise-grade environment variable management system with encryption, snapshots, and validation
1,308 lines (1,292 loc) • 107 kB
JavaScript
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(