{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "environment-variables-manager",
  "title": "Environment Variables Manager",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/environment-variables-manager/EnvironmentVariablesManager.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed, ref } from 'vue'\nimport {\n  AlertCircle,\n  Check,\n  Copy,\n  Download,\n  Eye,\n  EyeOff,\n  FileCode,\n  KeyRound,\n  Lock,\n  MoreHorizontal,\n  Pencil,\n  Plus,\n  Search,\n  ShieldCheck,\n  Trash2,\n  Upload,\n  X,\n} from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Checkbox } from '@/components/ui/checkbox'\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu'\nimport { Input } from '@/components/ui/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'\nimport { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'\nimport { Textarea } from '@/components/ui/textarea'\n\nexport type EnvironmentScope = 'production' | 'preview' | 'development'\nexport type EnvVarTypeFilter = 'all' | 'secret' | 'plain'\n\nexport interface EnvironmentVariable {\n  id: string\n  key: string\n  value: string\n  environments: EnvironmentScope[]\n  isSecret: boolean\n  updatedAt: string\n  updatedBy: string\n}\n\nconst defaultVariables: EnvironmentVariable[] = [\n  {\n    id: 'env-1',\n    key: 'DATABASE_URL',\n    value: 'postgresql://postgres:p4ssw0rd_secure_vault@db.prod.aws.internal:5432/primary_db',\n    environments: ['production', 'preview', 'development'],\n    isSecret: true,\n    updatedAt: 'Updated 2h ago',\n    updatedBy: 'alex.chen',\n  },\n  {\n    id: 'env-2',\n    key: 'STRIPE_SECRET_KEY',\n    value: 'mock_key_51NwY2xK9mPqL8vR4tZa0bCeFgHiJkLmNoPqRsTuVwXyZ',\n    environments: ['production'],\n    isSecret: true,\n    updatedAt: 'Updated 3d ago',\n    updatedBy: 'sarah.dev',\n  },\n  {\n    id: 'env-3',\n    key: 'NEXT_PUBLIC_APP_URL',\n    value: 'https://app.uipkge.dev',\n    environments: ['production', 'preview', 'development'],\n    isSecret: false,\n    updatedAt: 'Updated 5d ago',\n    updatedBy: 'uday.craft',\n  },\n  {\n    id: 'env-4',\n    key: 'REDIS_PASSWORD',\n    value: 'redis_auth_98f4b6201e9d4a87b32c',\n    environments: ['production', 'preview'],\n    isSecret: true,\n    updatedAt: 'Updated 1w ago',\n    updatedBy: 'alex.chen',\n  },\n  {\n    id: 'env-5',\n    key: 'SENTRY_DSN',\n    value: 'https://o45089@sentry.io/45089234871923',\n    environments: ['production', 'preview', 'development'],\n    isSecret: false,\n    updatedAt: 'Updated 2w ago',\n    updatedBy: 'sarah.dev',\n  },\n  {\n    id: 'env-6',\n    key: 'AWS_SECRET_ACCESS_KEY',\n    value: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',\n    environments: ['production'],\n    isSecret: true,\n    updatedAt: 'Updated 1mo ago',\n    updatedBy: 'infra-bot',\n  },\n]\n\ninterface Props {\n  initialVariables?: EnvironmentVariable[]\n  initialAddOpen?: boolean\n  initialImportOpen?: boolean\n  defaultEnvironment?: 'all' | EnvironmentScope\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  initialVariables: undefined,\n  initialAddOpen: false,\n  initialImportOpen: false,\n  defaultEnvironment: 'all',\n})\n\nconst variables = ref<EnvironmentVariable[]>(\n  props.initialVariables ? props.initialVariables.map((v) => ({ ...v })) : defaultVariables.map((v) => ({ ...v })),\n)\n\nconst activeTab = ref<'all' | EnvironmentScope>(props.defaultEnvironment)\nconst searchQuery = ref('')\nconst typeFilter = ref<EnvVarTypeFilter>('all')\nconst revealAll = ref(false)\nconst revealedMap = ref<Record<string, boolean>>({})\n\nconst isAddOpen = ref(props.initialAddOpen)\nconst isImportOpen = ref(props.initialImportOpen)\nconst editingVariable = ref<EnvironmentVariable | null>(null)\n\n// Add variable state\nconst newKey = ref('')\nconst newValue = ref('')\nconst newIsSecret = ref(true)\nconst newEnvs = ref<EnvironmentScope[]>(['production', 'preview', 'development'])\n\n// Import state\nconst importText = ref('')\nconst importEnvs = ref<EnvironmentScope[]>(['production', 'preview', 'development'])\nconst importEncryptAll = ref(true)\n\n// Copy state\nconst copiedId = ref<string | null>(null)\nlet copyTimer: number | undefined\n\n// Filtered variables\nconst filteredVariables = computed(() => {\n  return variables.value.filter((item) => {\n    // Environment filter\n    if (activeTab.value !== 'all' && !item.environments.includes(activeTab.value)) {\n      return false\n    }\n\n    // Type filter\n    if (typeFilter.value === 'secret' && !item.isSecret) return false\n    if (typeFilter.value === 'plain' && item.isSecret) return false\n\n    // Search query\n    if (searchQuery.value.trim()) {\n      const q = searchQuery.value.toLowerCase().trim()\n      const keyMatch = item.key.toLowerCase().includes(q)\n      const authorMatch = item.updatedBy.toLowerCase().includes(q)\n      if (!keyMatch && !authorMatch) return false\n    }\n\n    return true\n  })\n})\n\nconst counts = computed(() => {\n  return {\n    all: variables.value.length,\n    production: variables.value.filter((v) => v.environments.includes('production')).length,\n    preview: variables.value.filter((v) => v.environments.includes('preview')).length,\n    development: variables.value.filter((v) => v.environments.includes('development')).length,\n  }\n})\n\nfunction isRevealed(id: string): boolean {\n  if (revealAll.value) return true\n  return Boolean(revealedMap.value[id])\n}\n\nfunction toggleReveal(id: string) {\n  revealedMap.value[id] = !revealedMap.value[id]\n}\n\nfunction toggleRevealAll() {\n  revealAll.value = !revealAll.value\n  if (!revealAll.value) {\n    revealedMap.value = {}\n  }\n}\n\nasync function copyValue(text: string, id: string) {\n  try {\n    await navigator.clipboard.writeText(text)\n    copiedId.value = id\n    window.clearTimeout(copyTimer)\n    copyTimer = window.setTimeout(() => {\n      copiedId.value = null\n    }, 1600)\n  } catch {\n    // Clipboard fallback\n  }\n}\n\nfunction toggleNewEnv(env: EnvironmentScope) {\n  if (newEnvs.value.includes(env)) {\n    if (newEnvs.value.length > 1) {\n      newEnvs.value = newEnvs.value.filter((e) => e !== env)\n    }\n  } else {\n    newEnvs.value = [...newEnvs.value, env]\n  }\n}\n\nfunction resetAddForm() {\n  newKey.value = ''\n  newValue.value = ''\n  newIsSecret.value = true\n  newEnvs.value = ['production', 'preview', 'development']\n  isAddOpen.value = false\n}\n\nfunction handleAddVariable() {\n  const key = newKey.value.trim().toUpperCase()\n  const val = newValue.value.trim()\n  if (!key || !val) return\n\n  const newVar: EnvironmentVariable = {\n    id: `env-${Date.now()}`,\n    key,\n    value: val,\n    environments: [...newEnvs.value],\n    isSecret: newIsSecret.value,\n    updatedAt: 'Just now',\n    updatedBy: 'you',\n  }\n\n  variables.value.unshift(newVar)\n  resetAddForm()\n}\n\nfunction handleDuplicate(item: EnvironmentVariable) {\n  const duplicateVar: EnvironmentVariable = {\n    ...item,\n    id: `env-${Date.now()}`,\n    key: `${item.key}_COPY`,\n    updatedAt: 'Just now',\n    updatedBy: 'you',\n  }\n  const index = variables.value.findIndex((v) => v.id === item.id)\n  if (index >= 0) {\n    variables.value.splice(index + 1, 0, duplicateVar)\n  } else {\n    variables.value.unshift(duplicateVar)\n  }\n}\n\nfunction handleDelete(id: string) {\n  variables.value = variables.value.filter((v) => v.id !== id)\n}\n\nfunction openEdit(item: EnvironmentVariable) {\n  editingVariable.value = {\n    ...item,\n    environments: [...item.environments],\n  }\n}\n\nfunction saveEdit() {\n  if (!editingVariable.value) return\n  const key = editingVariable.value.key.trim().toUpperCase()\n  const val = editingVariable.value.value.trim()\n  if (!key || !val) return\n\n  const index = variables.value.findIndex((v) => v.id === editingVariable.value?.id)\n  if (index >= 0) {\n    variables.value[index] = {\n      ...editingVariable.value,\n      key,\n      value: val,\n      updatedAt: 'Just now',\n      updatedBy: 'you',\n    }\n  }\n  editingVariable.value = null\n}\n\nfunction toggleEditEnv(env: EnvironmentScope) {\n  if (!editingVariable.value) return\n  const current = editingVariable.value.environments\n  if (current.includes(env)) {\n    if (current.length > 1) {\n      editingVariable.value.environments = current.filter((e) => e !== env)\n    }\n  } else {\n    editingVariable.value.environments = [...current, env]\n  }\n}\n\nfunction toggleImportEnv(env: EnvironmentScope) {\n  if (importEnvs.value.includes(env)) {\n    if (importEnvs.value.length > 1) {\n      importEnvs.value = importEnvs.value.filter((e) => e !== env)\n    }\n  } else {\n    importEnvs.value = [...importEnvs.value, env]\n  }\n}\n\nconst parsedImportItems = computed(() => {\n  if (!importText.value.trim()) return []\n  const lines = importText.value.split('\\n')\n  const results: { key: string; value: string; isSecret: boolean }[] = []\n\n  for (const rawLine of lines) {\n    const line = rawLine.trim()\n    if (!line || line.startsWith('#')) continue\n    const eqIdx = line.indexOf('=')\n    if (eqIdx > 0) {\n      const k = line.slice(0, eqIdx).trim().toUpperCase()\n      let v = line.slice(eqIdx + 1).trim()\n      if ((v.startsWith('\"') && v.endsWith('\"')) || (v.startsWith(\"'\") && v.endsWith(\"'\"))) {\n        v = v.slice(1, -1)\n      }\n      if (k) {\n        const isSecret =\n          importEncryptAll.value ||\n          k.includes('SECRET') ||\n          k.includes('KEY') ||\n          k.includes('PASSWORD') ||\n          k.includes('TOKEN') ||\n          k.includes('PRIVATE') ||\n          k.includes('AUTH')\n        results.push({ key: k, value: v, isSecret })\n      }\n    }\n  }\n  return results\n})\n\nfunction handleImport() {\n  const items = parsedImportItems.value\n  if (items.length === 0) return\n\n  const newVars: EnvironmentVariable[] = items.map((item, index) => ({\n    id: `env-imported-${Date.now()}-${index}`,\n    key: item.key,\n    value: item.value,\n    environments: [...importEnvs.value],\n    isSecret: item.isSecret,\n    updatedAt: 'Imported just now',\n    updatedBy: 'you',\n  }))\n\n  variables.value = [...newVars, ...variables.value]\n  importText.value = ''\n  isImportOpen.value = false\n}\n\nfunction exportEnvFile() {\n  const lines = variables.value.map((v) => `# ${v.environments.join(', ')}\\n${v.key}=${v.value}`)\n  const blob = new Blob([lines.join('\\n\\n')], { type: 'text/plain;charset=utf-8' })\n  const url = URL.createObjectURL(blob)\n  const a = document.createElement('a')\n  a.href = url\n  a.download = '.env.production'\n  a.click()\n  URL.revokeObjectURL(url)\n}\n\nfunction maskString(val: string): string {\n  if (val.length <= 12) return '••••••••••••'\n  return '••••••••••••••••••••'\n}\n</script>\n\n<template>\n  <div data-slot=\"environment-variables-manager\" :class=\"cn('w-full space-y-6', props.class)\">\n    <!-- Header Section -->\n    <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n      <div class=\"space-y-1\">\n        <div class=\"flex items-center gap-2.5\">\n          <div class=\"bg-primary/10 text-primary grid size-8 place-items-center rounded-lg border\">\n            <KeyRound class=\"size-4\" />\n          </div>\n          <h2 class=\"text-xl font-semibold tracking-tight sm:text-2xl\">Environment Variables</h2>\n        </div>\n        <p class=\"text-muted-foreground text-sm\">\n          Manage encrypted secrets, API keys, and configuration for your deployments.\n        </p>\n      </div>\n\n      <div class=\"flex flex-wrap items-center gap-2\">\n        <Button variant=\"outline\" size=\"sm\" class=\"gap-1.5 shadow-xs\" @click=\"isImportOpen = true\">\n          <Upload class=\"size-3.5\" />\n          <span>Import .env</span>\n        </Button>\n        <Button\n          aria-label=\"Download attachment\"\n          variant=\"outline\"\n          size=\"sm\"\n          class=\"gap-1.5 shadow-xs\"\n          @click=\"exportEnvFile\"\n        >\n          <Download class=\"size-3.5\" />\n          <span>Export</span>\n        </Button>\n        <Button\n          size=\"sm\"\n          class=\"gap-1.5 shadow-xs\"\n          :variant=\"isAddOpen ? 'secondary' : 'default'\"\n          @click=\"isAddOpen = !isAddOpen\"\n        >\n          <Plus v-if=\"!isAddOpen\" class=\"size-4\" />\n          <X v-else class=\"size-4\" />\n          <span>{{ isAddOpen ? 'Close Form' : 'Add Variable' }}</span>\n        </Button>\n      </div>\n    </div>\n\n    <!-- Add Variable Expandable Card -->\n    <Card v-if=\"isAddOpen\" class=\"border-primary/30 bg-card/60 relative shadow-sm\">\n      <CardHeader class=\"pb-3\">\n        <div class=\"flex items-center justify-between\">\n          <div>\n            <CardTitle class=\"text-base font-semibold\">New Environment Variable</CardTitle>\n            <CardDescription class=\"text-xs\">\n              Add a new secret key or configuration parameter to your environment scopes.\n            </CardDescription>\n          </div>\n          <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Cancel adding variable\" @click=\"resetAddForm\">\n            <X class=\"size-4\" />\n          </Button>\n        </div>\n      </CardHeader>\n      <CardContent class=\"space-y-4\">\n        <div class=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Variable Key</label>\n            <Input\n              v-model=\"newKey\"\n              placeholder=\"e.g. STRIPE_SECRET_KEY\"\n              class=\"font-mono text-xs uppercase\"\n              @keydown.enter=\"handleAddVariable\"\n            />\n            <p class=\"text-muted-foreground text-xs\">Uppercase characters and underscores recommended.</p>\n          </div>\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Variable Value</label>\n            <Input\n              v-model=\"newValue\"\n              :type=\"newIsSecret ? 'password' : 'text'\"\n              placeholder=\"Enter secret token or value...\"\n              class=\"font-mono text-xs\"\n              @keydown.enter=\"handleAddVariable\"\n            />\n            <p class=\"text-muted-foreground text-xs\">Values are encrypted at rest with AES-256-GCM.</p>\n          </div>\n        </div>\n\n        <div class=\"flex flex-col gap-4 pt-1 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"space-y-2\">\n            <span class=\"text-foreground text-xs font-medium\">Environment Scopes</span>\n            <div class=\"flex flex-wrap items-center gap-3\">\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-2 rounded-md border px-2.5 py-1.5 text-xs transition-colors select-none\"\n                :class=\"newEnvs.includes('production') ? 'border-success/40 bg-success/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"newEnvs.includes('production')\"\n                  @update:model-value=\"toggleNewEnv('production')\"\n                />\n                <span class=\"text-success font-medium\">Production</span>\n              </label>\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-2 rounded-md border px-2.5 py-1.5 text-xs transition-colors select-none\"\n                :class=\"newEnvs.includes('preview') ? 'border-info/40 bg-info/5' : ''\"\n              >\n                <Checkbox :model-value=\"newEnvs.includes('preview')\" @update:model-value=\"toggleNewEnv('preview')\" />\n                <span class=\"text-info font-medium\">Preview</span>\n              </label>\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-2 rounded-md border px-2.5 py-1.5 text-xs transition-colors select-none\"\n                :class=\"newEnvs.includes('development') ? 'border-chart-2/40 bg-chart-2/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"newEnvs.includes('development')\"\n                  @update:model-value=\"toggleNewEnv('development')\"\n                />\n                <span class=\"text-chart-2 font-medium\">Development</span>\n              </label>\n            </div>\n          </div>\n\n          <div class=\"flex items-center gap-2\">\n            <Checkbox\n              id=\"new-is-secret\"\n              :model-value=\"newIsSecret\"\n              @update:model-value=\"newIsSecret = $event === true\"\n            />\n            <label for=\"new-is-secret\" class=\"cursor-pointer text-xs font-medium select-none\">\n              Encrypt as Secret\n            </label>\n          </div>\n        </div>\n\n        <div class=\"flex items-center justify-end gap-2 border-t pt-3\">\n          <Button aria-label=\"Cancel adding variable\" variant=\"ghost\" size=\"sm\" @click=\"resetAddForm\">Cancel</Button>\n          <Button size=\"sm\" :disabled=\"!newKey.trim() || !newValue.trim()\" @click=\"handleAddVariable\">\n            Save Variable\n          </Button>\n        </div>\n      </CardContent>\n    </Card>\n\n    <!-- Environment Tabs & Search / Filter Controls -->\n    <div class=\"space-y-3\">\n      <Tabs v-model=\"activeTab\" class=\"w-full\">\n        <TabsList class=\"w-full justify-start overflow-x-auto sm:w-auto\">\n          <TabsTrigger value=\"all\" class=\"text-xs\">\n            All Environments\n            <Badge variant=\"secondary\" class=\"ml-1.5 px-1.5 py-0 text-xs font-semibold\">{{ counts.all }}</Badge>\n          </TabsTrigger>\n          <TabsTrigger value=\"production\" class=\"text-xs\">\n            Production\n            <Badge variant=\"secondary\" class=\"ml-1.5 px-1.5 py-0 text-xs font-semibold\">{{ counts.production }}</Badge>\n          </TabsTrigger>\n          <TabsTrigger value=\"preview\" class=\"text-xs\">\n            Preview\n            <Badge variant=\"secondary\" class=\"ml-1.5 px-1.5 py-0 text-xs font-semibold\">{{ counts.preview }}</Badge>\n          </TabsTrigger>\n          <TabsTrigger value=\"development\" class=\"text-xs\">\n            Development\n            <Badge variant=\"secondary\" class=\"ml-1.5 px-1.5 py-0 text-xs font-semibold\">{{ counts.development }}</Badge>\n          </TabsTrigger>\n        </TabsList>\n      </Tabs>\n\n      <div class=\"flex flex-col gap-2.5 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"flex flex-1 flex-wrap items-center gap-2\">\n          <div class=\"relative max-w-sm min-w-[200px] flex-1\">\n            <Search\n              class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2\"\n            />\n            <Input\n              v-model=\"searchQuery\"\n              placeholder=\"Search variables by key...\"\n              class=\"h-8.5 pl-8 text-xs shadow-xs\"\n            />\n          </div>\n\n          <Select v-model=\"typeFilter\">\n            <SelectTrigger class=\"h-8.5 w-[140px] text-xs shadow-xs\">\n              <SelectValue placeholder=\"All types\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"all\">All Types</SelectItem>\n              <SelectItem value=\"secret\">Encrypted Secrets</SelectItem>\n              <SelectItem value=\"plain\">Plaintext</SelectItem>\n            </SelectContent>\n          </Select>\n        </div>\n\n        <div class=\"flex items-center gap-2\">\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"h-8.5 gap-1.5 text-xs shadow-xs\"\n            :class=\"revealAll ? 'border-primary text-primary' : ''\"\n            @click=\"toggleRevealAll\"\n          >\n            <EyeOff v-if=\"revealAll\" class=\"size-3.5\" />\n            <Eye v-else class=\"size-3.5\" />\n            <span>{{ revealAll ? 'Hide All' : 'Reveal All Values' }}</span>\n          </Button>\n        </div>\n      </div>\n    </div>\n\n    <!-- Variables Table -->\n    <div class=\"bg-card overflow-hidden rounded-lg border shadow-xs\">\n      <div class=\"overflow-x-auto\">\n        <Table>\n          <TableHeader>\n            <TableRow class=\"bg-muted/40 hover:bg-muted/40\">\n              <TableHead class=\"text-muted-foreground text-xs font-medium\">Variable Key</TableHead>\n              <TableHead class=\"text-muted-foreground text-xs font-medium\">Value</TableHead>\n              <TableHead class=\"text-muted-foreground text-xs font-medium\">Environments</TableHead>\n              <TableHead class=\"text-muted-foreground text-xs font-medium\">Type</TableHead>\n              <TableHead class=\"text-muted-foreground hidden text-xs font-medium md:table-cell\">Updated</TableHead>\n              <TableHead class=\"w-[70px] text-right text-xs font-medium\">\n                <span class=\"sr-only\">Actions</span>\n              </TableHead>\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            <TableRow\n              v-for=\"item in filteredVariables\"\n              :key=\"item.id\"\n              class=\"group hover:bg-muted/30 transition-colors\"\n            >\n              <!-- Key -->\n              <TableCell class=\"py-3.5\">\n                <div class=\"flex items-center gap-2\">\n                  <span class=\"text-foreground font-mono text-xs font-semibold tracking-tight select-all sm:text-sm\">\n                    {{ item.key }}\n                  </span>\n                  <button\n                    type=\"button\"\n                    aria-label=\"Copy variable key\"\n                    class=\"text-muted-foreground hover:text-foreground opacity-0 transition-opacity group-hover:opacity-100 focus:opacity-100\"\n                    @click=\"copyValue(item.key, `key-${item.id}`)\"\n                  >\n                    <Check v-if=\"copiedId === `key-${item.id}`\" class=\"text-success size-3.5\" />\n                    <Copy v-else class=\"size-3.5\" />\n                  </button>\n                </div>\n              </TableCell>\n\n              <!-- Value Mask / Plain -->\n              <TableCell class=\"py-3.5\">\n                <div class=\"flex max-w-[280px] items-center gap-1.5 sm:max-w-xs md:max-w-sm\">\n                  <code\n                    class=\"bg-muted/60 text-foreground max-w-[200px] min-w-0 truncate rounded border px-2 py-1 font-mono text-xs sm:max-w-[240px]\"\n                    :title=\"isRevealed(item.id) ? item.value : 'Masked secret value'\"\n                  >\n                    {{ isRevealed(item.id) ? item.value : maskString(item.value) }}\n                  </code>\n                  <Button\n                    variant=\"ghost\"\n                    size=\"icon-xs\"\n                    class=\"text-muted-foreground hover:text-foreground size-7 shrink-0\"\n                    :aria-label=\"isRevealed(item.id) ? 'Hide value' : 'Reveal value'\"\n                    @click=\"toggleReveal(item.id)\"\n                  >\n                    <EyeOff v-if=\"isRevealed(item.id)\" class=\"size-3.5\" />\n                    <Eye v-else class=\"size-3.5\" />\n                  </Button>\n                  <Button\n                    variant=\"ghost\"\n                    size=\"icon-xs\"\n                    class=\"text-muted-foreground hover:text-foreground size-7 shrink-0\"\n                    :aria-label=\"copiedId === item.id ? 'Copied' : 'Copy value'\"\n                    @click=\"copyValue(item.value, item.id)\"\n                  >\n                    <Check v-if=\"copiedId === item.id\" class=\"text-success size-3.5\" />\n                    <Copy v-else class=\"size-3.5\" />\n                  </Button>\n                </div>\n              </TableCell>\n\n              <!-- Environments -->\n              <TableCell class=\"py-3.5\">\n                <div class=\"flex flex-wrap items-center gap-1\">\n                  <Badge\n                    v-if=\"item.environments.includes('production')\"\n                    variant=\"outline\"\n                    class=\"border-success/30 bg-success/10 text-success px-2 py-0 text-xs font-medium\"\n                  >\n                    Production\n                  </Badge>\n                  <Badge\n                    v-if=\"item.environments.includes('preview')\"\n                    variant=\"outline\"\n                    class=\"border-info/30 bg-info/10 text-info px-2 py-0 text-xs font-medium\"\n                  >\n                    Preview\n                  </Badge>\n                  <Badge\n                    v-if=\"item.environments.includes('development')\"\n                    variant=\"outline\"\n                    class=\"border-chart-2/30 bg-chart-2/10 text-chart-2 px-2 py-0 text-xs font-medium\"\n                  >\n                    Development\n                  </Badge>\n                </div>\n              </TableCell>\n\n              <!-- Type -->\n              <TableCell class=\"py-3.5\">\n                <Badge\n                  v-if=\"item.isSecret\"\n                  variant=\"secondary\"\n                  class=\"border-warning/30 bg-warning/10 text-warning gap-1 px-2 py-0 text-xs font-medium\"\n                >\n                  <Lock class=\"size-3\" />\n                  Encrypted Secret\n                </Badge>\n                <Badge v-else variant=\"outline\" class=\"text-muted-foreground gap-1 px-2 py-0 text-xs font-medium\">\n                  <FileCode class=\"size-3\" />\n                  Plaintext\n                </Badge>\n              </TableCell>\n\n              <!-- Updated -->\n              <TableCell class=\"text-muted-foreground hidden py-3.5 text-xs whitespace-nowrap md:table-cell\">\n                <span>{{ item.updatedAt }}</span>\n                <span class=\"block text-xs opacity-75\">by {{ item.updatedBy }}</span>\n              </TableCell>\n\n              <!-- Actions Dropdown -->\n              <TableCell class=\"py-3.5 text-right\">\n                <DropdownMenu>\n                  <DropdownMenuTrigger as-child>\n                    <Button variant=\"ghost\" size=\"icon-sm\" class=\"text-muted-foreground hover:text-foreground size-8\">\n                      <MoreHorizontal class=\"size-4\" />\n                      <span class=\"sr-only\">Actions for {{ item.key }}</span>\n                    </Button>\n                  </DropdownMenuTrigger>\n                  <DropdownMenuContent align=\"end\" class=\"w-44 text-xs\">\n                    <DropdownMenuItem @click=\"openEdit(item)\">\n                      <Pencil class=\"mr-2 size-3.5\" />\n                      <span>Edit variable</span>\n                    </DropdownMenuItem>\n                    <DropdownMenuItem @click=\"handleDuplicate(item)\">\n                      <Copy class=\"mr-2 size-3.5\" />\n                      <span>Duplicate</span>\n                    </DropdownMenuItem>\n                    <DropdownMenuItem @click=\"copyValue(item.value, item.id)\">\n                      <Check v-if=\"copiedId === item.id\" class=\"text-success mr-2 size-3.5\" />\n                      <Copy v-else class=\"mr-2 size-3.5\" />\n                      <span>Copy value</span>\n                    </DropdownMenuItem>\n                    <DropdownMenuSeparator />\n                    <DropdownMenuItem class=\"text-destructive focus:text-destructive\" @click=\"handleDelete(item.id)\">\n                      <Trash2 class=\"mr-2 size-3.5\" />\n                      <span>Delete</span>\n                    </DropdownMenuItem>\n                  </DropdownMenuContent>\n                </DropdownMenu>\n              </TableCell>\n            </TableRow>\n\n            <!-- Empty State -->\n            <TableRow v-if=\"filteredVariables.length === 0\">\n              <TableCell colspan=\"6\" class=\"h-44 text-center\">\n                <div class=\"flex flex-col items-center justify-center space-y-2 py-6\">\n                  <div class=\"bg-muted text-muted-foreground grid size-10 place-items-center rounded-full border\">\n                    <AlertCircle class=\"size-5\" />\n                  </div>\n                  <p class=\"text-foreground text-sm font-medium\">No environment variables found</p>\n                  <p class=\"text-muted-foreground max-w-xs text-xs\">\n                    {{\n                      searchQuery || typeFilter !== 'all' || activeTab !== 'all'\n                        ? 'Try adjusting your filters or search keywords.'\n                        : 'Add your first environment secret or configuration key.'\n                    }}\n                  </p>\n                  <Button\n                    v-if=\"!searchQuery && typeFilter === 'all' && activeTab === 'all'\"\n                    size=\"sm\"\n                    class=\"mt-2 text-xs\"\n                    @click=\"isAddOpen = true\"\n                  >\n                    <Plus class=\"mr-1.5 size-3.5\" />\n                    Add Variable\n                  </Button>\n                </div>\n              </TableCell>\n            </TableRow>\n          </TableBody>\n        </Table>\n      </div>\n    </div>\n\n    <!-- Edit Variable Dialog -->\n    <Dialog :open=\"Boolean(editingVariable)\" @update:open=\"(open) => !open && (editingVariable = null)\">\n      <DialogContent v-if=\"editingVariable\" class=\"sm:max-w-md\">\n        <DialogHeader>\n          <DialogTitle>Edit Environment Variable</DialogTitle>\n          <DialogDescription> Update variable value and configured environment scopes. </DialogDescription>\n        </DialogHeader>\n        <div class=\"space-y-4 py-2\">\n          <div class=\"space-y-1.5\">\n            <label class=\"text-xs font-medium\">Variable Key</label>\n            <Input v-model=\"editingVariable.key\" class=\"font-mono text-xs uppercase\" />\n          </div>\n\n          <div class=\"space-y-1.5\">\n            <label class=\"text-xs font-medium\">Variable Value</label>\n            <Input\n              v-model=\"editingVariable.value\"\n              :type=\"editingVariable.isSecret ? 'password' : 'text'\"\n              class=\"font-mono text-xs\"\n            />\n          </div>\n\n          <div class=\"space-y-2\">\n            <span class=\"text-xs font-medium\">Environments</span>\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs select-none\"\n                :class=\"editingVariable.environments.includes('production') ? 'border-success/40 bg-success/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"editingVariable.environments.includes('production')\"\n                  @update:model-value=\"toggleEditEnv('production')\"\n                />\n                <span class=\"text-success font-medium\">Production</span>\n              </label>\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs select-none\"\n                :class=\"editingVariable.environments.includes('preview') ? 'border-info/40 bg-info/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"editingVariable.environments.includes('preview')\"\n                  @update:model-value=\"toggleEditEnv('preview')\"\n                />\n                <span class=\"text-info font-medium\">Preview</span>\n              </label>\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs select-none\"\n                :class=\"editingVariable.environments.includes('development') ? 'border-chart-2/40 bg-chart-2/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"editingVariable.environments.includes('development')\"\n                  @update:model-value=\"toggleEditEnv('development')\"\n                />\n                <span class=\"text-chart-2 font-medium\">Development</span>\n              </label>\n            </div>\n          </div>\n\n          <div class=\"flex items-center gap-2 pt-1\">\n            <Checkbox\n              id=\"edit-is-secret\"\n              :model-value=\"editingVariable.isSecret\"\n              @update:model-value=\"editingVariable.isSecret = $event === true\"\n            />\n            <label for=\"edit-is-secret\" class=\"cursor-pointer text-xs font-medium select-none\">\n              Encrypt as Secret\n            </label>\n          </div>\n        </div>\n        <DialogFooter>\n          <Button variant=\"ghost\" size=\"sm\" @click=\"editingVariable = null\">Cancel</Button>\n          <Button size=\"sm\" @click=\"saveEdit\">Save Changes</Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n\n    <!-- Import .env Dialog -->\n    <Dialog :open=\"isImportOpen\" @update:open=\"(open) => (isImportOpen = open)\">\n      <DialogContent class=\"sm:max-w-lg\">\n        <DialogHeader>\n          <div class=\"flex items-center gap-2\">\n            <div class=\"bg-primary/10 text-primary grid size-7 place-items-center rounded-md border\">\n              <Upload class=\"size-3.5\" />\n            </div>\n            <DialogTitle>Import .env File</DialogTitle>\n          </div>\n          <DialogDescription>\n            Paste your raw .env, .env.local, or .env.production file contents to bulk add variables.\n          </DialogDescription>\n        </DialogHeader>\n\n        <div class=\"space-y-4 py-2\">\n          <div class=\"space-y-1.5\">\n            <label class=\"text-xs font-medium\">.env File Content</label>\n            <Textarea\n              v-model=\"importText\"\n              rows=\"6\"\n              placeholder=\"DATABASE_URL=postgresql://user:pass@host:5432/db&#10;STRIPE_SECRET_KEY=mock_key_...&#10;NEXT_PUBLIC_APP_URL=https://app.example.com\"\n              class=\"font-mono text-xs\"\n            />\n          </div>\n\n          <div class=\"space-y-2\">\n            <span class=\"text-xs font-medium\">Assign to Environments</span>\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs select-none\"\n                :class=\"importEnvs.includes('production') ? 'border-success/40 bg-success/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"importEnvs.includes('production')\"\n                  @update:model-value=\"toggleImportEnv('production')\"\n                />\n                <span class=\"text-success font-medium\">Production</span>\n              </label>\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs select-none\"\n                :class=\"importEnvs.includes('preview') ? 'border-info/40 bg-info/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"importEnvs.includes('preview')\"\n                  @update:model-value=\"toggleImportEnv('preview')\"\n                />\n                <span class=\"text-info font-medium\">Preview</span>\n              </label>\n              <label\n                class=\"hover:bg-accent/50 flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs select-none\"\n                :class=\"importEnvs.includes('development') ? 'border-chart-2/40 bg-chart-2/5' : ''\"\n              >\n                <Checkbox\n                  :model-value=\"importEnvs.includes('development')\"\n                  @update:model-value=\"toggleImportEnv('development')\"\n                />\n                <span class=\"text-chart-2 font-medium\">Development</span>\n              </label>\n            </div>\n          </div>\n\n          <div class=\"flex items-center gap-2 pt-1\">\n            <Checkbox\n              id=\"import-encrypt-all\"\n              :model-value=\"importEncryptAll\"\n              @update:model-value=\"importEncryptAll = $event === true\"\n            />\n            <label for=\"import-encrypt-all\" class=\"cursor-pointer text-xs font-medium select-none\">\n              Encrypt all variables by default\n            </label>\n          </div>\n\n          <div\n            v-if=\"parsedImportItems.length > 0\"\n            class=\"bg-muted/60 flex items-center gap-2 rounded-md border px-3 py-2 text-xs\"\n          >\n            <ShieldCheck class=\"text-success size-4 shrink-0\" />\n            <span\n              >Ready to import <strong>{{ parsedImportItems.length }}</strong> variable(s).</span\n            >\n          </div>\n        </div>\n\n        <DialogFooter>\n          <Button variant=\"ghost\" size=\"sm\" @click=\"isImportOpen = false\">Cancel</Button>\n          <Button size=\"sm\" :disabled=\"parsedImportItems.length === 0\" @click=\"handleImport\">\n            Import {{ parsedImportItems.length > 0 ? `(${parsedImportItems.length})` : '' }}\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/EnvironmentVariablesManager.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/checkbox.json",
    "https://uipkge.dev/r/vue/dialog.json",
    "https://uipkge.dev/r/vue/dropdown-menu.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/select.json",
    "https://uipkge.dev/r/vue/table.json",
    "https://uipkge.dev/r/vue/tabs.json",
    "https://uipkge.dev/r/vue/textarea.json"
  ],
  "description": "Vercel and Supabase style environment variables and secrets manager with environment scoping, mask/reveal toggles, bulk import, and inline editing.",
  "categories": [
    "devops",
    "dashboard",
    "data",
    "developer"
  ]
}