Eidos

Installation Notice

The installation may currently fail. We recommend copying the code below and creating the extension manually in Eidos.

Table Schema Inspector

By: Mayne

Install Latest (v0.0.1)

view the table structure of a specified table, and the column names of fields in the database

import React, { useEffect, useState } from "react";
import { Database, Columns, Search, ChevronDown, Check } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "@/components/ui/command";
import { cn } from "@/lib/utils";

export default function SchemaInspector() {
  const [tables, setTables] = useState([]);
  const [selectedTable, setSelectedTable] = useState(null);
  const [columns, setColumns] = useState([]);
  const [loading, setLoading] = useState(false);
  const [open, setOpen] = useState(false);

  useEffect(() => {
    eidos.currentSpace.tree.findMany({ where: { type: "table" } }).then(setTables);
  }, []);

  const loadTableSchema = async (tableId) => {
    if (!tableId) {
      setSelectedTable(null);
      setColumns([]);
      return;
    }
    setLoading(true);
    try {
      const tableColumns = await eidos.currentSpace.column.findMany({ 
        where: { table_name: `tb_${tableId}` } 
      });
      setColumns(tableColumns);
      setSelectedTable(tableId);
    } catch (error) {
      console.error("Failed to load columns:", error);
    } finally {
      setLoading(false);
    }
  };

  const handleTableSelect = (tableId) => {
    loadTableSchema(tableId);
    setOpen(false);
  };

  return (
    <div className="min-h-screen bg-gray-50">
      <div className="max-w-6xl mx-auto px-6 py-8">
        <div className="mb-8">
          <h1 className="text-3xl font-light tracking-tight text-gray-900 mb-1">
            Schema Inspector
          </h1>
          <p className="text-gray-600">
            Explore your database structure
          </p>
        </div>

        <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
          <div className="lg:col-span-1">
            <div className="space-y-3">
              <div className="relative">
                <Popover open={open} onOpenChange={setOpen}>
                  <PopoverTrigger asChild>
                    <button className="w-full h-12 bg-white border-0 shadow-sm rounded-xl px-4 text-left flex items-center justify-between hover:shadow-md transition-shadow">
                      <span className="text-gray-900">
                        {selectedTable ? (tables.find(t => t.id === selectedTable)?.name || "Untitled") : "Select a table..."}
                      </span>
                      <ChevronDown className="w-4 h-4 text-gray-400" />
                    </button>
                  </PopoverTrigger>
                  <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0" align="start">
                    <Command>
                      <CommandInput 
                        placeholder="Search tables..." 
                        className="h-12 border-0 focus:ring-0"
                      />
                      <CommandEmpty className="py-6 text-center text-sm text-gray-500">
                        No tables found
                      </CommandEmpty>
                      <CommandGroup className="max-h-64 overflow-auto">
                        {tables.map((table) => (
                          <CommandItem
                            key={table.id}
                            value={table.name || "Untitled"}
                            onSelect={() => handleTableSelect(table.id)}
                            className="py-3 px-4 cursor-pointer"
                          >
                            <Check
                              className={cn(
                                "mr-2 h-4 w-4",
                                selectedTable === table.id ? "opacity-100" : "opacity-0"
                              )}
                            />
                            {table.name || "Untitled"}
                          </CommandItem>
                        ))}
                      </CommandGroup>
                    </Command>
                  </PopoverContent>
                </Popover>
              </div>

              <div className="mt-12">
                <div className="text-sm text-gray-500 mb-4">
                  {tables.length} tables available
                </div>
              </div>
            </div>
          </div>

          <div className="lg:col-span-2">
            <div className="bg-white rounded-xl shadow-sm">
              <div className="p-6">
                <div className="flex items-center justify-between mb-6">
                  <h2 className="text-2xl font-light text-gray-900">
                    {selectedTable ? (tables.find(t => t.id === selectedTable)?.name || "Untitled") : "No table selected"}
                  </h2>
                  <div className="text-sm text-gray-500">
                    {columns.length} {columns.length === 1 ? 'field' : 'fields'}
                  </div>
                </div>

                {loading && (
                  <div className="flex items-center justify-center py-16">
                    <div className="text-gray-400">Loading...</div>
                  </div>
                )}

                {!loading && selectedTable && columns.length === 0 && (
                  <div className="text-center py-16">
                    <div className="text-gray-400 mb-2">No fields found</div>
                    <div className="text-sm text-gray-500">This table appears to be empty</div>
                  </div>
                )}

                {!loading && selectedTable && columns.length > 0 && (
                  <div className="space-y-3">
                    {columns.map((column) => (
                      <div key={column.id} className="border-b border-gray-100 pb-4 last:border-0">
                        <div className="flex items-start justify-between mb-3">
                          <div>
                            <h3 className="text-lg font-medium text-gray-900 mb-1">
                              {column.name}
                            </h3>
                            <div className="text-sm text-gray-500">
                              {column.field}
                            </div>
                          </div>
                          <div className="text-sm font-mono text-gray-600 bg-gray-50 px-3 py-1 rounded-full">
                            {column.type}
                          </div>
                        </div>
                        
                        <div className="space-y-2 text-sm">
                          <div className="text-gray-600">
                            <span className="text-gray-400">Column:</span> {column.table_column_name}
                          </div>
                          {column.options && (
                            <div className="text-gray-600">
                              <span className="text-gray-400">Options:</span> {JSON.stringify(column.options)}
                            </div>
                          )}
                          {column.description && (
                            <div className="text-gray-600">
                              <span className="text-gray-400">Description:</span> {column.description}
                            </div>
                          )}
                        </div>
                      </div>
                    ))}
                  </div>
                )}

                {!selectedTable && (
                  <div className="text-center py-16">
                    <Database className="w-12 h-12 text-gray-300 mx-auto mb-4" />
                    <div className="text-gray-400">Select a table to explore its structure</div>
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

Information

Author
Mayne
Type
block
Latest Version
0.0.1
Last Updated
08/27/2025
Published
08/27/2025

Version History

  • v0.0.1 08/27/2025