From d0127cb2338780d3f0eefc410460340282ef2b9c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Jun 2025 16:18:44 +0000 Subject: [PATCH 01/20] feat: Revamp Dork Builder UI to Scratch-like interface This commit introduces a major overhaul of the Dork Builder user interface, transforming it into a more interactive, "Scratch-like" experience. Key changes include: 1. **Modular Architecture:** - Refactored `app/page.tsx` to delegate dork building logic to new, specialized components: - `DorkingInterface`: Manages overall dork building state and orchestrates interactions. - `CustomBlockManager`: Handles creation and validation of custom dork blocks. - Consolidated `DorkBlock` types and predefined block data into `types/dork.ts` and `lib/dork-utils.ts` respectively. 2. **Drag-and-Drop Interface:** - Implemented drag-and-drop functionality allowing you to drag blocks from a palette (`DorkBlockList`) into a workspace (`DorkBuilderArea`). - A unified `@dnd-kit` context manages both adding new blocks and reordering existing blocks within the workspace. 3. **Visual Styling:** - Applied "Scratch-like" styling to dork blocks, including: - Color-coding based on block type (e.g., `site`, `filetype`). - Improved block shapes, shadows, and input field appearance. - Enhanced visual feedback for dragging, dropping, and hover states. - Styled the workspace with a clearer drop zone and a subtle background pattern. 4. **Custom Block Enhancements:** - Refined the custom block creation process with improved form validation and feedback to you (toasts). - Implemented LocalStorage persistence for custom block templates, so your creations are saved across sessions. - Ensured custom blocks are correctly displayed in the palette and are draggable to the workspace. These changes significantly improve the usability, intuitiveness, and visual appeal of the dork builder, providing a more flexible and engaging experience for you. --- app/page.tsx | 327 +----------------- components/dork-builder/blocks/dork-block.tsx | 133 ++++--- .../dork-builder/custom-block-manager.tsx | 131 +++++++ components/dork-builder/dork-block-list.tsx | 154 ++++++--- components/dork-builder/dork-builder-area.tsx | 182 +++++----- components/dork-builder/dorking-interface.tsx | 238 +++++++++++++ types/dork.ts | 12 +- 7 files changed, 671 insertions(+), 506 deletions(-) create mode 100644 components/dork-builder/custom-block-manager.tsx create mode 100644 components/dork-builder/dorking-interface.tsx diff --git a/app/page.tsx b/app/page.tsx index 2019923..e52c4aa 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -22,102 +22,27 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select" -import { Textarea } from "@/components/ui/textarea" -import { Switch } from "@/components/ui/switch" -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +// import { Textarea } from "@/components/ui/textarea" // No longer directly used here +// import { Switch } from "@/components/ui/switch" // No longer directly used here +// import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" // No longer directly used here import { Card, CardContent, - CardDescription, + // CardDescription, // No longer directly used here CardHeader, CardTitle, } from "@/components/ui/card" +// import { DorkBlock, DorkBlockType } from "@/types/dork" // Types will be handled by DorkingInterface +// import { PREDEFINED_DORK_BLOCKS } from "@/lib/dork-utils" // Data will be handled by DorkingInterface +import { DorkingInterface } from "@/components/dork-builder/dorking-interface"; // Import the new main component -type DorkBlockType = - | "site" - | "inurl" - | "filetype" - | "intitle" - | "intext" - | "cache" - | "related" - | "custom" - -interface DorkBlock { - id: string - type: DorkBlockType - operator: string - value: string - placeholder: string - description: string -} - -const PREDEFINED_BLOCKS: Omit[] = [ - { - type: "site", - operator: "site:", - placeholder: "example.com", - description: "Search within a specific website or domain", - }, - { - type: "inurl", - operator: "inurl:", - placeholder: "admin", - description: "Search for pages with a specific word in the URL", - }, - { - type: "filetype", - operator: "filetype:", - placeholder: "pdf", - description: "Search for specific file types", - }, - { - type: "intitle", - operator: "intitle:", - placeholder: "index of", - description: "Search for pages with a specific word in the title", - }, - { - type: "intext", - operator: "intext:", - placeholder: "password", - description: "Search for pages containing specific text", - }, - { - type: "cache", - operator: "cache:", - placeholder: "example.com", - description: "Show Google’s cached version of a page", - }, - { - type: "related", - operator: "related:", - placeholder: "example.com", - description: "Find sites related to a given domain", - }, -] - -const generateId = () => Math.random().toString(36).substring(2, 9) +// const generateId = () => Math.random().toString(36).substring(2, 9) // ID generation will be handled internally by components export default function DorkingLab() { // Theme & Accessibility states const [theme, setTheme] = useState<"light" | "dark" | "high-contrast">("dark") const [fontSize, setFontSize] = useState(14) // px - // Blocks states - const [blocks, setBlocks] = useState([]) - const [customBlocks, setCustomBlocks] = useState([]) - - // Search engine: only one selectable - const [selectedEngine, setSelectedEngine] = useState< - "google" | "bing" | "duckduckgo" | "yahoo" - >("google") - - // New custom block input states - const [newCustomOperator, setNewCustomOperator] = useState("") - const [newCustomPlaceholder, setNewCustomPlaceholder] = useState("") - const [newCustomDescription, setNewCustomDescription] = useState("") - // Initialize theme from localStorage or system preference useEffect(() => { const savedTheme = localStorage.getItem("theme") as @@ -141,92 +66,6 @@ export default function DorkingLab() { document.documentElement.style.fontSize = fontSize + "px" }, [theme, fontSize]) - // Add a new block from predefined or custom - const addBlock = (block: Omit) => { - setBlocks([ - ...blocks, - { ...block, id: generateId(), value: "" }, // start empty value - ]) - } - - // Add a new custom block saved by user - const saveCustomBlock = () => { - if ( - !newCustomOperator.trim() || - !newCustomPlaceholder.trim() || - !newCustomDescription.trim() - ) { - alert("Please fill all fields to create a custom block") - return - } - if (!newCustomOperator.endsWith(":")) { - alert("Operator should end with a colon (:)") - return - } - - const newBlock: DorkBlock = { - id: generateId(), - type: "custom", - operator: newCustomOperator.trim(), - value: "", - placeholder: newCustomPlaceholder.trim(), - description: newCustomDescription.trim(), - } - setCustomBlocks([...customBlocks, newBlock]) - // reset form inputs - setNewCustomOperator("") - setNewCustomPlaceholder("") - setNewCustomDescription("") - } - - // Update block value on user input - const updateBlockValue = (id: string, newValue: string) => { - setBlocks( - blocks.map((block) => - block.id === id ? { ...block, value: newValue } : block - ) - ) - } - - // Remove a block - const removeBlock = (id: string) => { - setBlocks(blocks.filter((block) => block.id !== id)) - } - - // Build query string from blocks - const buildQuery = () => { - const parts = blocks - .map((b) => { - if (!b.value.trim()) return null - if (b.type === "custom") return `${b.operator}${b.value.trim()}` - return `${b.operator}${b.value.trim()}` - }) - .filter(Boolean) - return parts.join(" ") - } - - // Execute search on selected engine - const executeSearch = () => { - const query = buildQuery() - if (!query) { - alert("Add at least one block with a value") - return - } - const encodedQuery = encodeURIComponent(query) - const urls = { - google: `https://www.google.com/search?q=${encodedQuery}`, - bing: `https://www.bing.com/search?q=${encodedQuery}`, - duckduckgo: `https://duckduckgo.com/?q=${encodedQuery}`, - yahoo: `https://search.yahoo.com/search?p=${encodedQuery}`, - } - window.open(urls[selectedEngine], "_blank") - } - - // Handle search engine change (only one can be selected) - const handleEngineChange = (engine: typeof selectedEngine) => { - setSelectedEngine(engine) - } - // Toggle theme through options (dark, light, high-contrast) const cycleTheme = () => { if (theme === "dark") setTheme("light") @@ -254,148 +93,13 @@ export default function DorkingLab() { -
- {/* Blocks builder */} - - - Visual Dork Builder - - Add, edit, and combine dork blocks visually - - - -
- {[...PREDEFINED_BLOCKS, ...customBlocks].map((block) => ( - - ))} -
- - {blocks.length === 0 && ( -

Add blocks above to start building your dork.

- )} - -
- {blocks.map((block) => ( -
- - {block.operator} - - updateBlockValue(block.id, e.target.value)} - className="flex-grow rounded border border-gray-300 px-2 py-1 font-mono focus:outline-none focus:ring-2 focus:ring-primary force-black-text text-black" - aria-label={`Input for ${block.operator} block`} - /> - -
- ))} -
- - -
-
- - {/* Side Panel */} -
- {/* Search engine selection */} - - - Search Engine - - Select one search engine (only one active) - - - - {(["google", "bing", "duckduckgo", "yahoo"] as const).map( - (engine) => ( -
- handleEngineChange(engine)} - className="cursor-pointer" - /> - -
- ) - )} -
-
- - {/* Custom block creation */} - - - Create Custom Block - - Define a new operator block to reuse - - - - setNewCustomOperator(e.target.value)} - className="w-full rounded border border-gray-300 px-2 py-1 font-mono focus:outline-none focus:ring-2 focus:ring-primary force-black-text text-black" - aria-label="Custom operator" - /> - setNewCustomPlaceholder(e.target.value)} - className="w-full rounded border border-gray-300 px-2 py-1 font-mono focus:outline-none focus:ring-2 focus:ring-primary force-black-text text-black" - aria-label="Custom placeholder" - /> - setNewCustomDescription(e.target.value)} - className="w-full rounded border border-gray-300 px-2 py-1 font-mono focus:outline-none focus:ring-2 focus:ring-primary force-black-text text-black" - aria-label="Custom description" - /> - - - + {/* Render the DorkingInterface which now contains the main dork building UI and logic */} +
+ +
- {/* Accessibility & Theme */} + {/* Side Panel for Accessibility & Theme - kept in app/page.tsx */} +
Accessibility & Theme @@ -423,8 +127,7 @@ export default function DorkingLab() {

-
-
+
© 2025 DorkLabs • Made for OSINT enthusiasts
diff --git a/components/dork-builder/blocks/dork-block.tsx b/components/dork-builder/blocks/dork-block.tsx index c57b3b3..be20ea4 100644 --- a/components/dork-builder/blocks/dork-block.tsx +++ b/components/dork-builder/blocks/dork-block.tsx @@ -48,47 +48,97 @@ export function DorkBlock({ block, onUpdate, onRemove, isLast }: DorkBlockProps) const style = { transform: CSS.Transform.toString(transform), transition, - zIndex: isDragging ? 2 : 1, + zIndex: isDragging ? 100 : 1, // Higher zIndex when dragging + opacity: isDragging ? 0.9 : 1, }; + // Color coding based on block type + const blockColorClasses = () => { + switch (block.type) { + case 'site': + return 'bg-blue-500 hover:bg-blue-600 text-white border-blue-700'; + case 'filetype': + return 'bg-green-500 hover:bg-green-600 text-white border-green-700'; + case 'inurl': + return 'bg-orange-500 hover:bg-orange-600 text-white border-orange-700'; + case 'intitle': + return 'bg-yellow-500 hover:bg-yellow-600 text-black border-yellow-700'; // Text black for yellow bg + case 'intext': + return 'bg-sky-500 hover:bg-sky-600 text-white border-sky-700'; + case 'custom': + return 'bg-purple-500 hover:bg-purple-600 text-white border-purple-700'; + default: + return 'bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-white border-gray-400 dark:border-gray-500'; + } + }; + + const iconContainerColorClasses = () => { + // Slightly lighter/darker version for icon container, or specific colors + switch (block.type) { + case 'site': return 'bg-blue-400 dark:bg-blue-600'; + case 'filetype': return 'bg-green-400 dark:bg-green-600'; + case 'inurl': return 'bg-orange-400 dark:bg-orange-600'; + case 'intitle': return 'bg-yellow-400 dark:bg-yellow-600'; + case 'intext': return 'bg-sky-400 dark:bg-sky-600'; + case 'custom': return 'bg-purple-400 dark:bg-purple-600'; + default: return 'bg-gray-200 dark:bg-gray-500'; + } + } + const getIcon = () => { + const iconClass = "h-5 w-5"; // Slightly larger icons switch (block.icon) { - case 'globe': return ; - case 'link': return ; - case 'file': return ; - case 'heading': return ; - case 'text': return ; - case 'history': return ; - case 'link-2': return ; - case 'edit-3': return ; - default: return ; + case 'globe': return ; + case 'link': return ; + case 'file': return ; + case 'heading': return ; + case 'text': return ; + case 'history': return ; + case 'link-2': return ; + case 'edit-3': return ; // Default for custom and others + default: return ; } }; return ( -
-
- - {getIcon()} - - {block.type !== 'custom' && ( - - {block.operator} - + {/* Drag Handle - now part of the main block structure */} + + +
+ {getIcon()}
+ {/* Operator - ensure it's visible with new background */} + + {block.operator} + + onUpdate(block.id, e.target.value)} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} - className="flex-1 border-none bg-transparent px-2 py-1 focus-visible:ring-0 focus-visible:ring-offset-0" + className={cn( + "flex-1 px-2 py-1 h-auto rounded-md border-none focus-visible:ring-0 focus-visible:ring-offset-0", + "bg-white/20 dark:bg-black/20 placeholder:text-white/70 dark:placeholder:text-black/70", // Blended input + "focus:bg-white/30 dark:focus:bg-black/30" // Slightly more opaque on focus + )} /> @@ -104,33 +158,20 @@ export function DorkBlock({ block, onUpdate, onRemove, isLast }: DorkBlockProps) - -

Remove block

-
+

Remove block

- - - - - -

Drag to reorder

-
-
+ {/* Tooltip for drag handle is removed as the handle is now always visible and part of the block */}
); diff --git a/components/dork-builder/custom-block-manager.tsx b/components/dork-builder/custom-block-manager.tsx new file mode 100644 index 0000000..87ce214 --- /dev/null +++ b/components/dork-builder/custom-block-manager.tsx @@ -0,0 +1,131 @@ +"use client"; + +import { useState } from "react"; +import { DorkBlock, DorkBlockType } from "@/types/dork"; // DorkBlockType might not be needed directly +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; // Assuming you'll use this for text fields +import { Textarea } from "@/components/ui/textarea"; // Assuming you'll use this for description +import { PlusCircle } from "lucide-react"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { useToast } from "@/hooks/use-toast"; // Import useToast +import { generateId } from "@/lib/dork-utils"; // Import generateId from utils + +interface CustomBlockManagerProps { + onSaveCustomBlock: (newBlock: DorkBlock) => void; + customBlocks: DorkBlock[]; // To check for duplicate operators +} + +export function CustomBlockManager({ onSaveCustomBlock, customBlocks }: CustomBlockManagerProps) { + const { toast } = useToast(); + const [newCustomOperator, setNewCustomOperator] = useState(""); + const [newCustomPlaceholder, setNewCustomPlaceholder] = useState(""); + const [newCustomDescription, setNewCustomDescription] = useState(""); + const [operatorError, setOperatorError] = useState(null); + + const validateAndSave = () => { + const operator = newCustomOperator.trim(); + const placeholder = newCustomPlaceholder.trim(); + const description = newCustomDescription.trim(); + + setOperatorError(null); // Reset error + + if (!operator || !placeholder || !description) { + toast({ + title: "Validation Error", + description: "All fields are required to create a custom block.", + variant: "destructive", + }); + return; + } + + if (!operator.endsWith(":")) { + setOperatorError("Operator must end with a colon (e.g., myop:)."); + toast({ + title: "Validation Error", + description: "Operator must end with a colon (:).", + variant: "destructive", + }); + return; + } + + // Check for duplicate operator in existing custom blocks + if (customBlocks.some(block => block.operator === operator)) { + setOperatorError(`Operator "${operator}" already exists. Please use a unique operator.`); + toast({ + title: "Validation Error", + description: `Operator "${operator}" already exists. Please use a unique operator.`, + variant: "destructive", + }); + return; + } + + + const newBlock: DorkBlock = { + id: `custom-${generateId()}`, // Prefixed ID for custom block template + type: "custom", + operator: operator, + value: "", + placeholder: placeholder, + description: description, + icon: "edit-3", + }; + + onSaveCustomBlock(newBlock); + + toast({ + title: "Custom Block Saved!", + description: `The block "${operator}" has been added to your palette.`, + }); + + // Reset form inputs + setNewCustomOperator(""); + setNewCustomPlaceholder(""); + setNewCustomDescription(""); + setOperatorError(null); + }; + + // Render Structure + return ( + + + Create Custom Block + + Define a new operator block to reuse + + + +
+ { + setNewCustomOperator(e.target.value); + if (operatorError) setOperatorError(null); // Clear error on change + }} + aria-label="Custom operator" + className={operatorError ? "border-destructive focus-visible:ring-destructive" : ""} + /> + {operatorError &&

{operatorError}

} +
+ setNewCustomPlaceholder(e.target.value)} + aria-label="Custom placeholder" + /> + + + +
+

Search Engine

+ +
+ + + + + +
+

© 2024 Static Dork Builder

+
+ + + + diff --git a/js/blockManager.js b/js/blockManager.js new file mode 100644 index 0000000..d516a33 --- /dev/null +++ b/js/blockManager.js @@ -0,0 +1,231 @@ +import { qs, createElement } from './domUtils.js'; + +const predefinedBlocksData = [ + { id: 'site', type: 'site', operator: 'site:', placeholder: 'example.com', description: 'Search within a specific website.' }, + { id: 'filetype', type: 'filetype', operator: 'filetype:', placeholder: 'pdf', description: 'Search for specific file types.' }, + { id: 'inurl', type: 'inurl', operator: 'inurl:', placeholder: 'admin', description: 'Search for terms in the URL.' }, + { id: 'intitle', type: 'intitle', operator: 'intitle:', placeholder: 'login page', description: 'Search for terms in the page title.' }, + { id: 'intext', type: 'intext', operator: 'intext:', placeholder: 'username', description: 'Search for terms in the page content.' }, + { id: 'related', type: 'related', operator: 'related:', placeholder: 'example.com', description: 'Find sites related to a domain.' }, + { id: 'cache', type: 'cache', operator: 'cache:', placeholder: 'example.com', description: 'Show Google\'s cached version of a page.' } +]; + +let activeWorkspaceBlocks = []; +let customBlockTemplates = []; // Initialize customBlockTemplates array +let nextWorkspaceId = 1; +let nextCustomTemplateId = 1; // For custom block template IDs + +function generateUniqueCustomId() { + return `custom_tpl_${nextCustomTemplateId++}`; +} + +function generateUniqueId() { + return `ws_block_${nextWorkspaceId++}`; +} + +function updateBlockValue(workspaceBlockId, newValue) { + const blockIndex = activeWorkspaceBlocks.findIndex(b => b.id === workspaceBlockId); + if (blockIndex !== -1) { + activeWorkspaceBlocks[blockIndex].value = newValue; + updateQueryOutput(); // Update query output whenever a block's value changes + } +} + +function removeBlockFromWorkspace(workspaceBlockId) { + activeWorkspaceBlocks = activeWorkspaceBlocks.filter(b => b.id !== workspaceBlockId); + renderWorkspace(); + updateQueryOutput(); // Update query output after removing a block +} + +function addBlockToWorkspace(originalBlockData) { + const newBlock = { + ...originalBlockData, + id: generateUniqueId(), + value: '' + }; + activeWorkspaceBlocks.push(newBlock); + renderWorkspace(); + updateQueryOutput(); // Update query output after adding a block +} + +function renderWorkspace() { + const workspaceContainer = qs('#workspace-blocks'); + const emptyMessage = qs('.empty-workspace-message'); // Assumes this element exists outside the container if innerHTML is cleared + + if (!workspaceContainer) { // emptyMessage can be null if not found, handle that + console.error('Workspace container #workspace-blocks not found!'); + return; + } + + // Clear only block elements, keep the empty message if it's a child + // Or, ensure emptyMessage is outside workspaceContainer if workspaceContainer.innerHTML is used. + // For this implementation, let's assume emptyMessage is a direct child and we manage its display. + + // Remove all children except the empty message + while (workspaceContainer.firstChild && workspaceContainer.firstChild !== emptyMessage) { + workspaceContainer.removeChild(workspaceContainer.firstChild); + } + // If emptyMessage was removed, re-append it (or ensure it's always there and toggle display) + if (emptyMessage && !workspaceContainer.contains(emptyMessage)) { + // This logic is a bit complex; simpler to have emptyMessage outside or handle clearing carefully. + // For now, let's just clear all and re-add empty message if needed. + } + workspaceContainer.innerHTML = ''; // Simplest: clear all first + + if (activeWorkspaceBlocks.length === 0) { + if (emptyMessage) { + emptyMessage.style.display = 'block'; // Show message + workspaceContainer.appendChild(emptyMessage); // Re-add if cleared by innerHTML + } else { + // If empty message element itself is gone, create it (less ideal) + const newEmptyMessage = createElement('p', 'empty-workspace-message', 'Workspace is empty. Add blocks from the palette.'); + workspaceContainer.appendChild(newEmptyMessage); + } + } else { + if (emptyMessage) { + emptyMessage.style.display = 'none'; // Hide message + } + activeWorkspaceBlocks.forEach(block => { + const blockElement = createElement('div', ['dork-block', `dork-block-${block.type}`]); + blockElement.dataset.workspaceId = block.id; + + const operatorSpan = createElement('span', 'block-operator', block.operator); + blockElement.appendChild(operatorSpan); + + const inputElement = createElement('input'); + inputElement.type = 'text'; + inputElement.placeholder = block.placeholder; + inputElement.value = block.value; // Reflects current value from the model + inputElement.setAttribute('aria-label', `Value for ${block.operator}`); + inputElement.addEventListener('input', (event) => { + updateBlockValue(block.id, event.target.value); + }); + blockElement.appendChild(inputElement); + + const removeButton = createElement('button', 'remove-block-btn', 'X'); + removeButton.title = `Remove ${block.operator} block`; + removeButton.dataset.id = block.id; + removeButton.addEventListener('click', () => { + removeBlockFromWorkspace(block.id); + }); + blockElement.appendChild(removeButton); + + workspaceContainer.appendChild(blockElement); + }); + } +} + + +/** + * Renders the predefined and custom dork blocks into the palette. + */ +function renderPalette() { + const paletteContainer = qs('#palette-blocks'); + if (!paletteContainer) { + console.error('Palette container #palette-blocks not found!'); + return; + } + + // Helper function to create a palette block element + function createPaletteBlockElement(blockData, isCustom = false) { + // For custom blocks, type is 'custom'. For predefined, it's blockData.type. + const displayType = isCustom ? 'custom' : blockData.type; + // Ensure custom blocks get the .dork-block-custom style correctly + const typeClass = `dork-block-${displayType}`; + const blockElement = createElement('div', ['dork-block', typeClass]); + + const operatorText = createElement('span', 'block-operator', blockData.operator); + blockElement.appendChild(operatorText); + + // Hidden description span + // const descriptionSpan = createElement('span', 'block-description', blockData.description); + // descriptionSpan.style.display = 'none'; + // blockElement.appendChild(descriptionSpan); + + blockElement.dataset.blockId = blockData.id; // This ID is template ID (predefined or custom_tpl_X) + blockElement.dataset.blockType = displayType; + blockElement.dataset.operator = blockData.operator; + blockElement.dataset.placeholder = blockData.placeholder; + blockElement.dataset.description = blockData.description; + + blockElement.title = blockData.description; + + blockElement.tabIndex = 0; + blockElement.setAttribute('role', 'button'); + blockElement.setAttribute('aria-label', `Add ${blockData.operator} block: ${blockData.description}`); + + const handlePaletteBlockActivation = () => { + // addBlockToWorkspace expects the full block data structure to copy from. + // blockData already holds this (either from predefinedBlocksData or customBlockTemplates) + addBlockToWorkspace(blockData); + }; + + blockElement.addEventListener('click', handlePaletteBlockActivation); + blockElement.addEventListener('keydown', (event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + handlePaletteBlockActivation(); + } + }); + return blockElement; + } + + paletteContainer.innerHTML = ''; // Clear existing blocks + + // Render predefined blocks + predefinedBlocksData.forEach(blockData => { + const blockElement = createPaletteBlockElement(blockData, false); + paletteContainer.appendChild(blockElement); + }); + + // Render custom blocks + customBlockTemplates.forEach(blockData => { + const blockElement = createPaletteBlockElement(blockData, true); + paletteContainer.appendChild(blockElement); + }); +} + +/** + * Initializes the block manager (e.g., loads predefined blocks). + */ +export function initBlockManager() { + console.log("Block Manager Initialized"); + renderPalette(); + renderWorkspace(); + updateQueryOutput(); +} + +export function addCustomBlockTemplate(blockDetails) { + const newCustomBlock = { + id: generateUniqueCustomId(), + type: 'custom', // Specific type for custom blocks + operator: blockDetails.operator, + placeholder: blockDetails.placeholder || 'Enter value...', // Default placeholder + description: blockDetails.description, + // icon: 'edit-3' // Default icon for custom type, handled by styling/rendering if needed + }; + customBlockTemplates.push(newCustomBlock); + renderPalette(); // Re-render palette to include the new custom block + // TODO: In a later step, save customBlockTemplates to LocalStorage + // console.log('Custom block templates:', customBlockTemplates); // For debugging +} + +export function generateQueryString() { + return activeWorkspaceBlocks + .filter(block => block.value && block.value.trim() !== '') // Only include blocks with a value + .map(block => { + // If block.operator is empty (e.g. for a raw custom string from GHDB), just use the value + return block.operator ? `${block.operator}${block.value.trim()}` : block.value.trim(); + }) + .join(' '); +} + +export function updateQueryOutput() { + const queryOutputTextarea = qs('#query-output'); + if (queryOutputTextarea) { + queryOutputTextarea.value = generateQueryString(); + } +} + +// Export activeWorkspaceBlocks is not chosen for encapsulation. +// Getter function is also not strictly necessary if updateQueryOutput is the main interface for this. diff --git a/js/customBlock.js b/js/customBlock.js new file mode 100644 index 0000000..d52a681 --- /dev/null +++ b/js/customBlock.js @@ -0,0 +1,53 @@ +// js/customBlock.js +import { qs } from './domUtils.js'; +import { addCustomBlockTemplate } from './blockManager.js'; + +export function initCustomBlockEditor() { + const form = qs('#custom-block-form'); + if (!form) { + console.error('Custom block form #custom-block-form not found!'); + return; + } + + form.addEventListener('submit', (event) => { + event.preventDefault(); + const operatorInput = qs('#custom-op'); + const placeholderInput = qs('#custom-val-placeholder'); + const descriptionInput = qs('#custom-desc'); + + // Check if inputs are found (robustness) + if (!operatorInput || !placeholderInput || !descriptionInput) { + console.error('One or more custom block form input fields not found!'); + alert('Error in custom block form setup. Please check console.'); + return; + } + + const operator = operatorInput.value.trim(); + const placeholder = placeholderInput.value.trim(); // Placeholder is optional for the block logic, but form might require it + const description = descriptionInput.value.trim(); + + if (!operator || !description) { + alert('Operator and Description are required for custom blocks.'); + return; + } + + // Basic validation for operator format (optional, but good) + if (!operator.includes(':')) { + // Consider if this should be a warning or prevent submission + // For now, a simple alert. Could be enhanced with non-modal feedback. + const confirmNoColon = confirm('Operator does not include a colon (e.g., "myop:"). This is a common convention. Proceed anyway?'); + if (!confirmNoColon) { + operatorInput.focus(); + return; + } + } + + addCustomBlockTemplate({ operator, placeholder, description }); + + form.reset(); // Clear the form fields + operatorInput.focus(); // Focus on the first field for easier multiple additions + + // Optionally, add a success message/toast here + // console.log('Custom block template added:', { operator, placeholder, description }); + }); +} diff --git a/js/domUtils.js b/js/domUtils.js new file mode 100644 index 0000000..eb71c13 --- /dev/null +++ b/js/domUtils.js @@ -0,0 +1,39 @@ +/** + * Alias for document.querySelector. + * @param {string} selector - CSS selector. + * @returns {Element|null} + */ +export function qs(selector) { + return document.querySelector(selector); +} + +/** + * Alias for document.querySelectorAll. + * @param {string} selector - CSS selector. + * @returns {NodeListOf} + */ +export function qsa(selector) { + return document.querySelectorAll(selector); +} + +/** + * Creates an element with a given tag, class name, and text content. + * @param {string} tagName - The HTML tag name. + * @param {string|string[]} [className] - Optional class name or array of class names. + * @param {string} [textContent] - Optional text content. + * @returns {HTMLElement} + */ +export function createElement(tagName, className, textContent) { + const element = document.createElement(tagName); + if (className) { + if (Array.isArray(className)) { + element.classList.add(...className); + } else { + element.classList.add(className); + } + } + if (textContent) { + element.textContent = textContent; + } + return element; +} diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..65b0c3e --- /dev/null +++ b/js/main.js @@ -0,0 +1,63 @@ +import { initBlockManager, updateQueryOutput } from './blockManager.js'; +import { initCustomBlockEditor } from './customBlock.js'; // Import +import { qs } from './domUtils.js'; + +/** + * Main function to initialize the application. + */ +function main() { + console.log("Static Dork Builder Initializing..."); + initBlockManager(); + initCustomBlockEditor(); // Initialize custom block editor + + const headerTitle = qs('header h1'); + if (headerTitle) { + console.log('Found header:', headerTitle.textContent); + } + + // Setup Search Engine Logic and Execute Search Button + const searchEngineSelect = qs('#search-engine-select'); + const executeSearchButton = qs('#execute-search-button'); + const queryOutputTextarea = qs('#query-output'); // Already used by updateQueryOutput + + let selectedSearchEngine = 'google'; // Default + + if (searchEngineSelect) { + selectedSearchEngine = searchEngineSelect.value; // Initialize with current value + searchEngineSelect.addEventListener('change', (event) => { + selectedSearchEngine = event.target.value; + // console.log('Search engine changed to:', selectedSearchEngine); // For debugging + }); + } + + if (executeSearchButton && queryOutputTextarea) { + executeSearchButton.addEventListener('click', () => { + const query = queryOutputTextarea.value.trim(); // Get query from textarea + if (!query) { + alert('Query is empty. Add some blocks and values, or write a custom query.'); + return; + } + + let searchUrl = ''; + switch (selectedSearchEngine) { + case 'bing': + searchUrl = `https://www.bing.com/search?q=${encodeURIComponent(query)}`; + break; + case 'duckduckgo': + searchUrl = `https://duckduckgo.com/?q=${encodeURIComponent(query)}`; + break; + case 'google': + default: + searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`; + break; + } + // console.log('Executing search:', searchUrl); // For debugging + window.open(searchUrl, '_blank'); // Open in a new tab + }); + } + + // Other initializations will go here +} + +// Wait for the DOM to be fully loaded before running the main script +document.addEventListener('DOMContentLoaded', main); diff --git a/style.css b/style.css new file mode 100644 index 0000000..f02c0d3 --- /dev/null +++ b/style.css @@ -0,0 +1,409 @@ +/* ==== Global Resets and Base Styles ==== */ +:root { + --primary-color: #4a90e2; /* A muted blue */ + --primary-hover-color: #3a7bc8; + --secondary-color: #5cb85c; /* Green for save/add */ + --secondary-hover-color: #4cae4c; + --destructive-color: #d9534f; + --destructive-hover-color: #c9302c; + + --bg-color: #22272e; /* Dark gray */ + --bg-panel-color: #2d333b; /* Slightly lighter for panels */ + --bg-element-color: #373e47; /* For inputs, buttons within panels */ + --bg-block-color: #4a525c; /* Default block background */ + --bg-block-hover-color: #5a626c; + + --text-color: #c9d1d9; /* Light gray/off-white */ + --text-muted-color: #8b949e; + --text-on-primary-color: #ffffff; + --text-on-secondary-color: #ffffff; + + --border-color: #444c56; + --border-light-color: #586069; + --border-focus-color: var(--primary-color); + + --font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + --font-family-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + + --border-radius: 6px; + --shadow-sm: 0 1px 2px rgba(0,0,0,0.15); + --shadow-md: 0 3px 6px rgba(0,0,0,0.2); +} + +* { + box-sizing: border-box; +} + +body { + font-family: var(--font-family-sans); + margin: 0; + background-color: var(--bg-color); + color: var(--text-color); + display: flex; + flex-direction: column; + min-height: 100vh; + line-height: 1.6; +} + +/* ==== Header & Footer ==== */ +header, footer { + background-color: var(--bg-panel-color); + color: var(--text-color); + padding: 1rem; + text-align: center; + border-bottom: 1px solid var(--border-color); +} +footer { + border-top: 1px solid var(--border-color); + border-bottom: none; + margin-top: auto; /* Pushes footer to bottom */ +} +header h1, footer p { + margin: 0; +} + +/* ==== Main Application Container ==== */ +.app-container { + display: flex; + flex-grow: 1; + padding: 1rem; + gap: 1rem; + overflow: hidden; /* Prevent overall page scroll if panels handle their own */ +} + +/* ==== Panels ==== */ +.left-panel, .right-panel { + flex: 0 0 25%; /* flex-grow, flex-shrink, flex-basis */ + background-color: var(--bg-panel-color); + padding: 1rem; + border-radius: var(--border-radius); + box-shadow: var(--shadow-sm); + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.workspace-area { + flex: 0 0 50%; + background-color: var(--bg-panel-color); + padding: 1rem; + border-radius: var(--border-radius); + box-shadow: var(--shadow-sm); + overflow-y: auto; + display: flex; /* For vertical stacking if needed */ + flex-direction: column; +} + +/* ==== Sections within Panels ==== */ +section { + /* Sections can have their own background if needed for more separation */ + /* background-color: var(--bg-element-color); */ + /* padding: 0.75rem; */ + /* border-radius: var(--border-radius); */ + /* border: 1px solid var(--border-color); */ +} +section h2 { + font-size: 1.1rem; + color: var(--text-color); + margin-top: 0; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border-light-color); +} + +/* ==== Block Lists (Palette, Workspace, GHDB Results) ==== */ +.block-list { + min-height: 80px; + padding: 0.5rem; + border-radius: var(--border-radius); + background-color: var(--bg-color); /* Slightly darker than panel for contrast */ + border: 1px dashed var(--border-light-color); /* Default state */ + flex-grow: 1; /* For GHDB results and workspace blocks */ + overflow-y: auto; /* If many blocks */ +} +.active-blocks-container { /* Specific to workspace */ + border-style: solid; /* Solid for active area */ +} +.active-blocks-container.drag-over { /* Class to be added by JS */ + border-color: var(--primary-color); + background-color: rgba(var(--primary-color), 0.1); /* Needs JS to parse color */ +} +.empty-workspace-message { + color: var(--text-muted-color); + text-align: center; + padding: 2rem 1rem; + font-style: italic; +} + +/* ==== Dork Block Styling (.dork-block) ==== */ +.dork-block { + background-color: var(--bg-block-color); + border: 1px solid var(--border-light-color); /* Subtle border around block */ + border-left-width: 5px; /* Colored left border for type indication */ + border-radius: var(--border-radius); + padding: 0.6rem 0.8rem; + margin-bottom: 0.6rem; + display: flex; + align-items: center; + gap: 0.6rem; + box-shadow: var(--shadow-sm); + transition: background-color 0.2s ease, box-shadow 0.2s ease; + color: var(--text-color); /* Default text color */ +} +.dork-block:hover { + background-color: var(--bg-block-hover-color); + box-shadow: var(--shadow-md); +} + +/* Specific block type colors (applied by JS by adding classes like .dork-block-site) */ +.dork-block-site { border-left-color: #3b82f6; } /* Blue */ +.dork-block-filetype { border-left-color: #22c55e; } /* Green */ +.dork-block-inurl { border-left-color: #f97316; } /* Orange */ +.dork-block-custom { border-left-color: #8b5cf6; } /* Purple */ +.dork-block-ghdb { border-left-color: #ec4899; } /* Pink for GHDB imported */ + +.dork-block .drag-handle { + cursor: grab; + color: var(--text-muted-color); + padding: 0.2rem; +} +.dork-block .drag-handle:hover { + color: var(--text-color); +} + +.dork-block .block-operator { + font-family: var(--font-family-mono); + font-weight: bold; + font-size: 0.9em; + white-space: nowrap; +} + +.dork-block input[type="text"] { + flex-grow: 1; + padding: 0.4rem 0.6rem; + border: 1px solid var(--border-color); + border-radius: calc(var(--border-radius) - 2px); + background-color: var(--bg-element-color); + color: var(--text-color); + font-size: 0.9em; +} +.dork-block input[type="text"]:focus { + outline: none; + border-color: var(--border-focus-color); + box-shadow: 0 0 0 2px rgba(var(--primary-color), 0.3); /* Needs JS for color parsing */ +} + +.dork-block .remove-block-btn { + background-color: var(--destructive-color); + color: var(--text-on-primary-color); /* Assuming white/light text on dark red */ + border: none; + padding: 0.25rem 0.5rem; + border-radius: var(--border-radius); /* Use global border-radius */ + cursor: pointer; + font-weight: bold; + font-size: 0.8em; /* Slightly smaller font for 'X' */ + line-height: 1; /* Ensure 'X' is centered if padding makes it look off */ + margin-left: 0.5rem; + transition: background-color 0.2s ease; +} +.dork-block .remove-block-btn:hover { + background-color: var(--destructive-hover-color); +} + +/* Palette block specific styling (if needed to be smaller/different) */ +#palette-blocks .dork-block { + padding: 0.4rem 0.6rem; + cursor: grab; +} +#palette-blocks .dork-block .block-operator { + font-size: 0.85em; +} + + +/* ==== Form Elements & Controls ==== */ +label { + display: block; + margin-bottom: 0.3rem; + font-size: 0.9rem; + color: var(--text-muted-color); +} + +input[type="text"], +input[type="search"], +textarea, +select { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + background-color: var(--bg-element-color); + color: var(--text-color); + font-size: 0.9rem; + margin-bottom: 0.75rem; /* Default spacing */ +} +input[type="text"]:focus, +input[type="search"]:focus, +textarea:focus, +select:focus { + outline: none; + border-color: var(--border-focus-color); + box-shadow: 0 0 0 2px rgba(var(--primary-color), 0.3); /* Needs JS for color parsing */ +} + +textarea#query-output { + font-family: var(--font-family-mono); + min-height: 80px; + resize: vertical; +} + +button, +button[type="submit"] { + padding: 0.6rem 1rem; + border: none; + border-radius: var(--border-radius); + cursor: pointer; + font-size: 0.9rem; + font-weight: 500; + transition: background-color 0.2s ease; + background-color: var(--primary-color); + color: var(--text-on-primary-color); +} +button:hover, +button[type="submit"]:hover { + background-color: var(--primary-hover-color); +} + +#custom-block-form button[type="submit"] { + background-color: var(--secondary-color); + color: var(--text-on-secondary-color); + width: 100%; +} +#custom-block-form button[type="submit"]:hover { + background-color: var(--secondary-hover-color); +} + +#ghdb-search-area { + display: flex; + gap: 0.5rem; + margin-bottom: 1rem; /* More space below search */ +} +#ghdb-search-area input[type="search"] { + flex-grow: 1; + margin-bottom: 0; /* Remove default margin */ +} +#ghdb-search-area button { + flex-shrink: 0; /* Prevent button from shrinking */ +} + +#ghdb-pagination { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 0.75rem; + margin-top: 0.75rem; + border-top: 1px solid var(--border-light-color); + font-size: 0.9rem; +} +#ghdb-pagination button { + background-color: var(--bg-element-color); + color: var(--text-color); + border: 1px solid var(--border-color); +} +#ghdb-pagination button:hover { + background-color: var(--border-color); +} +#ghdb-pagination button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +#execute-search-button { + width: 100%; + margin-top: 1rem; + padding: 0.75rem 1rem; /* Larger execute button */ + font-size: 1rem; +} + +/* GHDB Entry specific styling (inside #ghdb-results) */ +.ghdb-entry { + background-color: var(--bg-element-color); + padding: 0.75rem; + margin-bottom: 0.5rem; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + font-size: 0.9rem; +} +.ghdb-entry h3 { + font-size: 1rem; + margin-top: 0; + margin-bottom: 0.25rem; + color: var(--primary-color); +} +.ghdb-entry .category, .ghdb-entry .date { + font-size: 0.8rem; + color: var(--text-muted-color); + margin-bottom: 0.25rem; +} +.ghdb-entry .dork-string { + font-family: var(--font-family-mono); + background-color: var(--bg-color); + padding: 0.25rem 0.5rem; + border-radius: calc(var(--border-radius) - 2px); + display: block; + margin-bottom: 0.5rem; + word-break: break-all; +} +.ghdb-entry .import-button { + background-color: var(--secondary-color); + color: var(--text-on-secondary-color); + font-size: 0.8rem; + padding: 0.25rem 0.6rem; + float: right; /* Example positioning */ +} +.ghdb-entry .import-button:hover { + background-color: var(--secondary-hover-color); +} +.ghdb-entry .ghdb-link { + font-size: 0.8rem; + color: var(--primary-color); + text-decoration: none; +} +.ghdb-entry .ghdb-link:hover { + text-decoration: underline; +} + +/* Clearfix for floated elements if any (like import button) */ +.ghdb-entry::after { + content: ""; + clear: both; + display: table; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .app-container { + flex-direction: column; + } + .left-panel, .right-panel, .workspace-area { + width: 100%; + flex: 0 0 auto; /* Reset flex property for stacking */ + max-height: none; /* Allow full height in column layout */ + } +} + +/* Scrollbar styling (optional, for webkit browsers) */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-track { + background: var(--bg-panel-color); +} +::-webkit-scrollbar-thumb { + background: var(--border-light-color); + border-radius: 4px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--border-color); +} From 87d3f2ec34520912ba6b5e2f940253a71bcd2a85 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Jun 2025 18:03:46 +0000 Subject: [PATCH 07/20] I've attempted to clean up unused Next.js files after the static rewrite. This addresses the cleanup of files from the previous Next.js project structure following the rewrite to a static HTML, CSS, and vanilla JavaScript application. Changes: - I successfully deleted several obsolete root-level configuration files specific to the Next.js/React/TypeScript setup, including: - components.json - next.config.js, next.config.mjs - postcss.config.js, postcss.config.mjs - tailwind.config.ts - tsconfig.json - .eslintrc.json - I verified that essential files for the new static application (index.html, style.css, js/*, public/*, package.json, etc.) are intact. Known Issue: - I could not delete obsolete directories from the Next.js structure (e.g., app/, components/, hooks/, lib/, styles/, types/). These directories currently remain in the repository but are not used by the static application. The primary goal of this cleanup was to remove unneeded configuration clutter. You might need to perform further manual cleanup of the directories. --- .eslintrc.json | 3 - .github/workflows/deploy.yml | 2 +- components.json | 21 ----- components/dork-builder/dorking-interface.tsx | 16 ++-- next.config.js | 11 --- next.config.mjs | 18 ----- postcss.config.js | 6 -- postcss.config.mjs | 8 -- tailwind.config.ts | 81 ------------------- tsconfig.json | 27 ------- 10 files changed, 9 insertions(+), 184 deletions(-) delete mode 100644 .eslintrc.json delete mode 100644 components.json delete mode 100644 next.config.js delete mode 100644 next.config.mjs delete mode 100644 postcss.config.js delete mode 100644 postcss.config.mjs delete mode 100644 tailwind.config.ts delete mode 100644 tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b313115..12175d2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -50,4 +50,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/components.json b/components.json deleted file mode 100644 index d9ef0ae..0000000 --- a/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "tailwind.config.ts", - "css": "app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} \ No newline at end of file diff --git a/components/dork-builder/dorking-interface.tsx b/components/dork-builder/dorking-interface.tsx index bfa3610..d0607d1 100644 --- a/components/dork-builder/dorking-interface.tsx +++ b/components/dork-builder/dorking-interface.tsx @@ -90,7 +90,7 @@ export function DorkingInterface() { }; setActiveBlocks(prev => [...prev, newBlockInstance]); }; - + // Renamed for clarity from placeholder const updateActiveBlockValue = (id: string, newValue: string) => { setActiveBlocks(prevBlocks => @@ -171,7 +171,7 @@ export function DorkingInterface() { } return; } - + // Potentially other scenarios, like dragging from palette directly onto another block in workspace (to insert) // For now, only handle direct drop on workspace area or reorder within workspace }; @@ -206,12 +206,12 @@ export function DorkingInterface() { GHDB - - @@ -232,7 +232,7 @@ export function DorkingInterface() { activeBlocks={activeBlocks} onUpdateBlockValue={updateActiveBlockValue} onRemoveBlock={removeActiveBlock} - onClearAllBlocks={handleClearAllActiveBlocks} + onClearAllBlocks={handleClearAllActiveBlocks} // onReorderBlocks prop is removed />
@@ -267,7 +267,7 @@ export function DorkingInterface() { )}
- + -
- - - {/* Render the DorkingInterface which now contains the main dork building UI and logic */} -
- -
- - {/* Side Panel for Accessibility & Theme - kept in app/page.tsx */} -
- - - Accessibility & Theme - - - - setFontSize(Number(e.target.value))} - className="w-full" - aria-valuemin={12} - aria-valuemax={24} - aria-valuenow={fontSize} - aria-label="Font size slider" - /> -

- Use the theme button on top-right to cycle between Dark, Light, - and High Contrast themes. -

-
-
-
-
- © 2025 DorkLabs • Made for OSINT enthusiasts -
- - ) -} From 969c0f2b9e2496187740edce17f4224633c1e765 Mon Sep 17 00:00:00 2001 From: pr0ff3 <150958256+sPROFFEs@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:11:45 +0200 Subject: [PATCH 09/20] Delete components directory --- components/dork-builder/blocks/dork-block.tsx | 178 ---- .../dork-builder/custom-block-manager.tsx | 131 --- components/dork-builder/dork-block-list.tsx | 150 ---- components/dork-builder/dork-builder-area.tsx | 130 --- components/dork-builder/dorking-interface.tsx | 281 ------- components/dork-builder/ghdb-explorer.tsx | 213 ----- components/dork-builder/query-output.tsx | 100 --- .../dork-builder/search-engine-selector.tsx | 39 - components/theme-provider.tsx | 11 - components/ui/accordion.tsx | 58 -- components/ui/alert-dialog.tsx | 141 ---- components/ui/alert.tsx | 59 -- components/ui/aspect-ratio.tsx | 7 - components/ui/avatar.tsx | 50 -- components/ui/badge.tsx | 36 - components/ui/breadcrumb.tsx | 115 --- components/ui/button.tsx | 56 -- components/ui/calendar.tsx | 66 -- components/ui/card.tsx | 79 -- components/ui/carousel.tsx | 262 ------ components/ui/chart.tsx | 365 --------- components/ui/checkbox.tsx | 30 - components/ui/collapsible.tsx | 11 - components/ui/command.tsx | 153 ---- components/ui/context-menu.tsx | 200 ----- components/ui/dialog.tsx | 122 --- components/ui/drawer.tsx | 118 --- components/ui/dropdown-menu.tsx | 200 ----- components/ui/form.tsx | 178 ---- components/ui/hover-card.tsx | 29 - components/ui/input-otp.tsx | 71 -- components/ui/input.tsx | 22 - components/ui/label.tsx | 26 - components/ui/menubar.tsx | 236 ------ components/ui/navigation-menu.tsx | 128 --- components/ui/pagination.tsx | 117 --- components/ui/popover.tsx | 31 - components/ui/progress.tsx | 28 - components/ui/radio-group.tsx | 44 - components/ui/resizable.tsx | 45 -- components/ui/scroll-area.tsx | 48 -- components/ui/select.tsx | 160 ---- components/ui/separator.tsx | 31 - components/ui/sheet.tsx | 140 ---- components/ui/sidebar.tsx | 763 ------------------ components/ui/skeleton.tsx | 15 - components/ui/slider.tsx | 28 - components/ui/sonner.tsx | 31 - components/ui/switch.tsx | 29 - components/ui/table.tsx | 117 --- components/ui/tabs.tsx | 55 -- components/ui/textarea.tsx | 22 - components/ui/theme-toggle.tsx | 39 - components/ui/toast.tsx | 129 --- components/ui/toaster.tsx | 35 - components/ui/toggle-group.tsx | 61 -- components/ui/toggle.tsx | 45 -- components/ui/tooltip.tsx | 30 - components/ui/use-mobile.tsx | 19 - components/ui/use-toast.ts | 194 ----- 60 files changed, 6307 deletions(-) delete mode 100644 components/dork-builder/blocks/dork-block.tsx delete mode 100644 components/dork-builder/custom-block-manager.tsx delete mode 100644 components/dork-builder/dork-block-list.tsx delete mode 100644 components/dork-builder/dork-builder-area.tsx delete mode 100644 components/dork-builder/dorking-interface.tsx delete mode 100644 components/dork-builder/ghdb-explorer.tsx delete mode 100644 components/dork-builder/query-output.tsx delete mode 100644 components/dork-builder/search-engine-selector.tsx delete mode 100644 components/theme-provider.tsx delete mode 100644 components/ui/accordion.tsx delete mode 100644 components/ui/alert-dialog.tsx delete mode 100644 components/ui/alert.tsx delete mode 100644 components/ui/aspect-ratio.tsx delete mode 100644 components/ui/avatar.tsx delete mode 100644 components/ui/badge.tsx delete mode 100644 components/ui/breadcrumb.tsx delete mode 100644 components/ui/button.tsx delete mode 100644 components/ui/calendar.tsx delete mode 100644 components/ui/card.tsx delete mode 100644 components/ui/carousel.tsx delete mode 100644 components/ui/chart.tsx delete mode 100644 components/ui/checkbox.tsx delete mode 100644 components/ui/collapsible.tsx delete mode 100644 components/ui/command.tsx delete mode 100644 components/ui/context-menu.tsx delete mode 100644 components/ui/dialog.tsx delete mode 100644 components/ui/drawer.tsx delete mode 100644 components/ui/dropdown-menu.tsx delete mode 100644 components/ui/form.tsx delete mode 100644 components/ui/hover-card.tsx delete mode 100644 components/ui/input-otp.tsx delete mode 100644 components/ui/input.tsx delete mode 100644 components/ui/label.tsx delete mode 100644 components/ui/menubar.tsx delete mode 100644 components/ui/navigation-menu.tsx delete mode 100644 components/ui/pagination.tsx delete mode 100644 components/ui/popover.tsx delete mode 100644 components/ui/progress.tsx delete mode 100644 components/ui/radio-group.tsx delete mode 100644 components/ui/resizable.tsx delete mode 100644 components/ui/scroll-area.tsx delete mode 100644 components/ui/select.tsx delete mode 100644 components/ui/separator.tsx delete mode 100644 components/ui/sheet.tsx delete mode 100644 components/ui/sidebar.tsx delete mode 100644 components/ui/skeleton.tsx delete mode 100644 components/ui/slider.tsx delete mode 100644 components/ui/sonner.tsx delete mode 100644 components/ui/switch.tsx delete mode 100644 components/ui/table.tsx delete mode 100644 components/ui/tabs.tsx delete mode 100644 components/ui/textarea.tsx delete mode 100644 components/ui/theme-toggle.tsx delete mode 100644 components/ui/toast.tsx delete mode 100644 components/ui/toaster.tsx delete mode 100644 components/ui/toggle-group.tsx delete mode 100644 components/ui/toggle.tsx delete mode 100644 components/ui/tooltip.tsx delete mode 100644 components/ui/use-mobile.tsx delete mode 100644 components/ui/use-toast.ts diff --git a/components/dork-builder/blocks/dork-block.tsx b/components/dork-builder/blocks/dork-block.tsx deleted file mode 100644 index be20ea4..0000000 --- a/components/dork-builder/blocks/dork-block.tsx +++ /dev/null @@ -1,178 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { DorkBlock as DorkBlockType } from "@/types/dork"; -import { cn } from "@/lib/utils"; -import { Input } from "@/components/ui/input"; -import { - Globe, - Link, - File, - Heading, - FileText, - History, - Link2, - Edit3, - X, - GripVertical, -} from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { useSortable } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; - -interface DorkBlockProps { - block: DorkBlockType; - onUpdate: (id: string, value: string) => void; - onRemove: (id: string) => void; - isLast: boolean; -} - -export function DorkBlock({ block, onUpdate, onRemove, isLast }: DorkBlockProps) { - const [isFocused, setIsFocused] = useState(false); - - const { - attributes, - listeners, - setNodeRef, - transform, - transition, - isDragging, - } = useSortable({ id: block.id }); - - const style = { - transform: CSS.Transform.toString(transform), - transition, - zIndex: isDragging ? 100 : 1, // Higher zIndex when dragging - opacity: isDragging ? 0.9 : 1, - }; - - // Color coding based on block type - const blockColorClasses = () => { - switch (block.type) { - case 'site': - return 'bg-blue-500 hover:bg-blue-600 text-white border-blue-700'; - case 'filetype': - return 'bg-green-500 hover:bg-green-600 text-white border-green-700'; - case 'inurl': - return 'bg-orange-500 hover:bg-orange-600 text-white border-orange-700'; - case 'intitle': - return 'bg-yellow-500 hover:bg-yellow-600 text-black border-yellow-700'; // Text black for yellow bg - case 'intext': - return 'bg-sky-500 hover:bg-sky-600 text-white border-sky-700'; - case 'custom': - return 'bg-purple-500 hover:bg-purple-600 text-white border-purple-700'; - default: - return 'bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-white border-gray-400 dark:border-gray-500'; - } - }; - - const iconContainerColorClasses = () => { - // Slightly lighter/darker version for icon container, or specific colors - switch (block.type) { - case 'site': return 'bg-blue-400 dark:bg-blue-600'; - case 'filetype': return 'bg-green-400 dark:bg-green-600'; - case 'inurl': return 'bg-orange-400 dark:bg-orange-600'; - case 'intitle': return 'bg-yellow-400 dark:bg-yellow-600'; - case 'intext': return 'bg-sky-400 dark:bg-sky-600'; - case 'custom': return 'bg-purple-400 dark:bg-purple-600'; - default: return 'bg-gray-200 dark:bg-gray-500'; - } - } - - const getIcon = () => { - const iconClass = "h-5 w-5"; // Slightly larger icons - switch (block.icon) { - case 'globe': return ; - case 'link': return ; - case 'file': return ; - case 'heading': return ; - case 'text': return ; - case 'history': return ; - case 'link-2': return ; - case 'edit-3': return ; // Default for custom and others - default: return ; - } - }; - - return ( - -
- {/* Drag Handle - now part of the main block structure */} - - -
- {getIcon()} -
- - {/* Operator - ensure it's visible with new background */} - - {block.operator} - - - onUpdate(block.id, e.target.value)} - onFocus={() => setIsFocused(true)} - onBlur={() => setIsFocused(false)} - className={cn( - "flex-1 px-2 py-1 h-auto rounded-md border-none focus-visible:ring-0 focus-visible:ring-offset-0", - "bg-white/20 dark:bg-black/20 placeholder:text-white/70 dark:placeholder:text-black/70", // Blended input - "focus:bg-white/30 dark:focus:bg-black/30" // Slightly more opaque on focus - )} - /> - - - - - -

Remove block

-
- - {/* Tooltip for drag handle is removed as the handle is now always visible and part of the block */} -
-
- ); -} diff --git a/components/dork-builder/custom-block-manager.tsx b/components/dork-builder/custom-block-manager.tsx deleted file mode 100644 index 87ce214..0000000 --- a/components/dork-builder/custom-block-manager.tsx +++ /dev/null @@ -1,131 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { DorkBlock, DorkBlockType } from "@/types/dork"; // DorkBlockType might not be needed directly -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; // Assuming you'll use this for text fields -import { Textarea } from "@/components/ui/textarea"; // Assuming you'll use this for description -import { PlusCircle } from "lucide-react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { useToast } from "@/hooks/use-toast"; // Import useToast -import { generateId } from "@/lib/dork-utils"; // Import generateId from utils - -interface CustomBlockManagerProps { - onSaveCustomBlock: (newBlock: DorkBlock) => void; - customBlocks: DorkBlock[]; // To check for duplicate operators -} - -export function CustomBlockManager({ onSaveCustomBlock, customBlocks }: CustomBlockManagerProps) { - const { toast } = useToast(); - const [newCustomOperator, setNewCustomOperator] = useState(""); - const [newCustomPlaceholder, setNewCustomPlaceholder] = useState(""); - const [newCustomDescription, setNewCustomDescription] = useState(""); - const [operatorError, setOperatorError] = useState(null); - - const validateAndSave = () => { - const operator = newCustomOperator.trim(); - const placeholder = newCustomPlaceholder.trim(); - const description = newCustomDescription.trim(); - - setOperatorError(null); // Reset error - - if (!operator || !placeholder || !description) { - toast({ - title: "Validation Error", - description: "All fields are required to create a custom block.", - variant: "destructive", - }); - return; - } - - if (!operator.endsWith(":")) { - setOperatorError("Operator must end with a colon (e.g., myop:)."); - toast({ - title: "Validation Error", - description: "Operator must end with a colon (:).", - variant: "destructive", - }); - return; - } - - // Check for duplicate operator in existing custom blocks - if (customBlocks.some(block => block.operator === operator)) { - setOperatorError(`Operator "${operator}" already exists. Please use a unique operator.`); - toast({ - title: "Validation Error", - description: `Operator "${operator}" already exists. Please use a unique operator.`, - variant: "destructive", - }); - return; - } - - - const newBlock: DorkBlock = { - id: `custom-${generateId()}`, // Prefixed ID for custom block template - type: "custom", - operator: operator, - value: "", - placeholder: placeholder, - description: description, - icon: "edit-3", - }; - - onSaveCustomBlock(newBlock); - - toast({ - title: "Custom Block Saved!", - description: `The block "${operator}" has been added to your palette.`, - }); - - // Reset form inputs - setNewCustomOperator(""); - setNewCustomPlaceholder(""); - setNewCustomDescription(""); - setOperatorError(null); - }; - - // Render Structure - return ( - - - Create Custom Block - - Define a new operator block to reuse - - - -
- { - setNewCustomOperator(e.target.value); - if (operatorError) setOperatorError(null); // Clear error on change - }} - aria-label="Custom operator" - className={operatorError ? "border-destructive focus-visible:ring-destructive" : ""} - /> - {operatorError &&

{operatorError}

} -
- setNewCustomPlaceholder(e.target.value)} - aria-label="Custom placeholder" - /> -