[dyad] Added keyword counting feature - wrote 2 file(s)

This commit is contained in:
[dyad]
2026-01-20 14:19:08 +01:00
parent 393cb4cf7a
commit 0932ac6447
2 changed files with 57 additions and 3 deletions

View File

@@ -15,7 +15,7 @@ export interface HeadlineNode {
children: HeadlineNode[];
}
export async function extractMetaData(url: string) {
export async function extractMetaData(url: string, keyword?: string) {
if (!url) {
return { error: "URL is required." };
}
@@ -112,6 +112,15 @@ export async function extractMetaData(url: string) {
path.push(node);
});
let keywordCount: number | null = null;
const trimmedKeyword = keyword?.trim();
if (trimmedKeyword) {
const bodyText = $("body").text();
const regex = new RegExp(trimmedKeyword, "gi");
const matches = bodyText.match(regex);
keywordCount = matches ? matches.length : 0;
}
return {
data: {
title,
@@ -119,6 +128,8 @@ export async function extractMetaData(url: string) {
image,
faq: faqData.length > 0 ? faqData : null,
headlines: headlines.length > 0 ? headlines : null,
keyword: trimmedKeyword || null,
keywordCount,
},
};
} catch (error) {

View File

@@ -5,7 +5,15 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent } from "@/components/ui/card";
import { Globe, Edit, Check, Loader2, X, ImageOff } from "lucide-react";
import {
Globe,
Edit,
Check,
Loader2,
X,
ImageOff,
Search,
} from "lucide-react";
import { extractMetaData, type HeadlineNode } from "@/app/actions";
import { LengthIndicator } from "./length-indicator";
import { CopyButton } from "./copy-button";
@@ -21,10 +29,13 @@ interface MetaData {
image?: string | null;
faq?: { question: string; answer: string }[] | null;
headlines?: HeadlineNode[] | null;
keyword?: string | null;
keywordCount?: number | null;
}
export function MetaForm() {
const [url, setUrl] = useState("");
const [keyword, setKeyword] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [metaData, setMetaData] = useState<MetaData | null>(null);
@@ -53,7 +64,7 @@ export function MetaForm() {
setIsEditingDescription(false);
setImageError(false);
const result = await extractMetaData(url);
const result = await extractMetaData(url, keyword);
if (result.error) {
setError(result.error);
@@ -66,6 +77,7 @@ export function MetaForm() {
const handleClear = () => {
setUrl("");
setKeyword("");
setLoading(false);
setError(null);
setMetaData(null);
@@ -89,6 +101,17 @@ export function MetaForm() {
onChange={(e) => setUrl(e.target.value)}
/>
</div>
<div className="relative w-full sm:w-auto">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<Input
name="keyword"
type="text"
placeholder="Keyword (optional)"
className="pl-10 h-12 text-base rounded-lg shadow-sm sm:w-64"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
</div>
<Button
type="submit"
disabled={loading}
@@ -276,6 +299,26 @@ export function MetaForm() {
</p>
)}
</div>
{metaData.keyword &&
typeof metaData.keywordCount === "number" && (
<div>
<h3 className="font-semibold text-card-foreground">
Keyword: "{metaData.keyword}"
</h3>
<p className="text-sm text-muted-foreground mt-1 mb-2">
The number of times this keyword appears on the
page.
</p>
<div className="text-foreground bg-muted p-3 rounded-md min-h-[40px] flex items-center">
<span className="font-mono text-2xl font-bold">
{metaData.keywordCount}
</span>
<span className="ml-2 text-muted-foreground">
occurrences
</span>
</div>
</div>
)}
</div>
</div>
</CardContent>