[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[]; children: HeadlineNode[];
} }
export async function extractMetaData(url: string) { export async function extractMetaData(url: string, keyword?: string) {
if (!url) { if (!url) {
return { error: "URL is required." }; return { error: "URL is required." };
} }
@@ -112,6 +112,15 @@ export async function extractMetaData(url: string) {
path.push(node); 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 { return {
data: { data: {
title, title,
@@ -119,6 +128,8 @@ export async function extractMetaData(url: string) {
image, image,
faq: faqData.length > 0 ? faqData : null, faq: faqData.length > 0 ? faqData : null,
headlines: headlines.length > 0 ? headlines : null, headlines: headlines.length > 0 ? headlines : null,
keyword: trimmedKeyword || null,
keywordCount,
}, },
}; };
} catch (error) { } catch (error) {

View File

@@ -5,7 +5,15 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent } from "@/components/ui/card"; 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 { extractMetaData, type HeadlineNode } from "@/app/actions";
import { LengthIndicator } from "./length-indicator"; import { LengthIndicator } from "./length-indicator";
import { CopyButton } from "./copy-button"; import { CopyButton } from "./copy-button";
@@ -21,10 +29,13 @@ interface MetaData {
image?: string | null; image?: string | null;
faq?: { question: string; answer: string }[] | null; faq?: { question: string; answer: string }[] | null;
headlines?: HeadlineNode[] | null; headlines?: HeadlineNode[] | null;
keyword?: string | null;
keywordCount?: number | null;
} }
export function MetaForm() { export function MetaForm() {
const [url, setUrl] = useState(""); const [url, setUrl] = useState("");
const [keyword, setKeyword] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [metaData, setMetaData] = useState<MetaData | null>(null); const [metaData, setMetaData] = useState<MetaData | null>(null);
@@ -53,7 +64,7 @@ export function MetaForm() {
setIsEditingDescription(false); setIsEditingDescription(false);
setImageError(false); setImageError(false);
const result = await extractMetaData(url); const result = await extractMetaData(url, keyword);
if (result.error) { if (result.error) {
setError(result.error); setError(result.error);
@@ -66,6 +77,7 @@ export function MetaForm() {
const handleClear = () => { const handleClear = () => {
setUrl(""); setUrl("");
setKeyword("");
setLoading(false); setLoading(false);
setError(null); setError(null);
setMetaData(null); setMetaData(null);
@@ -89,6 +101,17 @@ export function MetaForm() {
onChange={(e) => setUrl(e.target.value)} onChange={(e) => setUrl(e.target.value)}
/> />
</div> </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 <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
@@ -276,6 +299,26 @@ export function MetaForm() {
</p> </p>
)} )}
</div> </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>
</div> </div>
</CardContent> </CardContent>