Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/do-wdr-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ git push origin main --tags
### 4. Wait for CI/CD

The tag push triggers `.github/workflows/release.yml` which:

- Runs Python and Rust test suites
- Builds binaries for Linux, macOS, and Windows
- Generates build attestations
Expand Down
5 changes: 0 additions & 5 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ name = "compaction_bench"
harness = false
path = "benches/compaction_bench.rs"

[[bench]]
name = "quality_bench"
harness = false
path = "benches/quality_bench.rs"

[[bin]]
name = "do-wdr"
path = "src/main.rs"
Expand Down
54 changes: 0 additions & 54 deletions cli/benches/quality_bench.rs

This file was deleted.

32 changes: 13 additions & 19 deletions cli/src/quality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,25 @@ pub struct QualityScore {
pub acceptable: bool,
}

use regex::Regex;
use std::collections::HashSet;
use std::sync::OnceLock;

static NOISY_REGEX: OnceLock<Regex> = OnceLock::new();

pub fn score_content(markdown: &str, links: &[String], threshold: f32) -> QualityScore {
let trimmed = markdown.trim();
let len = trimmed.len();

let too_short = len < 500;
let missing_links = links.is_empty();

let mut num_lines = 0;
let mut unique_lines = HashSet::new();
for line in trimmed.lines() {
num_lines += 1;
unique_lines.insert(line.trim());
}
let unique_count = unique_lines.len();
let duplicate_heavy = num_lines > 0 && unique_count < std::cmp::max(5, num_lines / 3);

let noisy_re = NOISY_REGEX
.get_or_init(|| Regex::new("(?i)cookie|subscribe|javascript|log in|sign up").unwrap());
let noisy_count = noisy_re.find_iter(trimmed).count();
let lines: Vec<&str> = trimmed.lines().collect();
let unique_lines = lines
.iter()
.copied()
.collect::<std::collections::HashSet<_>>()
.len();
let duplicate_heavy = !lines.is_empty() && unique_lines < std::cmp::max(5, lines.len() / 3);
let lower = trimmed.to_lowercase();
let noisy_count = lower.matches("cookie").count()
+ lower.matches("subscribe").count()
+ lower.matches("javascript").count()
+ lower.matches("log in").count()
+ lower.matches("sign up").count();
let noisy = noisy_count > 6;

let has_frontmatter = trimmed.starts_with("---")
Expand Down
248 changes: 74 additions & 174 deletions web/app/components/MainContent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"use client";

import Link from "next/link";
import { MainHeader } from "@/app/components/MainHeader";
import { SearchSection } from "@/app/components/SearchSection";
import { MetadataBar } from "@/app/components/MetadataBar";
import ResultCard from "@/app/components/ResultCard";
import { ProviderResult } from "@/lib/results";
import type { ProviderResult } from "@/lib/results";

interface MainContentProps {
mobileMenuOpen: boolean;
Expand Down Expand Up @@ -36,191 +38,87 @@
isUrl: boolean;
}

export default function MainContent({
mobileMenuOpen,
setMobileMenuOpen,
query,
setQuery,
handleSubmit,
loading,
inputRef,
error,
result,
setResult,
setError,
providerStatus,
setProviderStatus,
sourceProvider,
setSourceProvider,
resolveTime,
setResolveTime,
qualityScore,
setQualityScore,
parsedResults,
setParsedResults,
viewRaw,
setViewRaw,
helpfulIds,
toggleHelpful,
handleCopyResult,
handleCardCopy,
copied,
isUrl,
}: MainContentProps) {
const charCount = result.length;
export const MainContent = (props: MainContentProps) => {
const {
setMobileMenuOpen,
query,
setQuery,
handleSubmit,
loading,
inputRef,
error,
result,
setResult,
setError,
providerStatus,
setProviderStatus,
sourceProvider,
setSourceProvider,
resolveTime,
setResolveTime,
qualityScore,
setQualityScore,
parsedResults,
setParsedResults,
viewRaw,
setViewRaw,
helpfulIds,
toggleHelpful,
handleCopyResult,
handleCardCopy,
copied,
isUrl,
} = props;

const handleClear = () => {

Check warning on line 73 in web/app/components/MainContent.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

web/app/components/MainContent.tsx#L73

Non-serializable expression must be wrapped with $(...)
setQuery("");
setResult("");
setError("");
setProviderStatus(null);
setResolveTime(null);
setSourceProvider(null);
setQualityScore(null);
setParsedResults([]);
setViewRaw(false);
inputRef.current?.focus();
};

return (
<div id="main-content" className="flex-1 flex flex-col min-h-0" tabIndex={-1}>
{/* Header */}
<div className="border-b-2 border-border-muted p-2 flex items-center justify-between min-h-[44px]">
<div className="flex items-center gap-2">
{/* Hamburger menu - mobile only */}
<button
onClick={() => setMobileMenuOpen(true)}
className="lg:hidden p-2 text-text-muted hover:text-foreground min-h-[44px] min-w-[44px] flex items-center justify-center"
aria-label="Open menu"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<span className="text-[11px] text-text-muted">do-web-doc-resolver</span>
</div>
<Link href="/help" className="text-[11px] text-text-muted hover:text-accent min-h-[44px] flex items-center px-2">
Help
</Link>
</div>
<MainHeader setMobileMenuOpen={setMobileMenuOpen} />

{/* Input */}
<div className="border-b-2 border-border-muted p-4">
<div className="flex items-center gap-4">
<label htmlFor="search-input" className="sr-only">
URL or search query
</label>
<div className="flex-1 relative">
<input
id="search-input"
ref={inputRef}
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSubmit()}
placeholder="URL or search query..."
aria-invalid={!!error}
aria-errormessage={error ? "search-error" : undefined}
className="w-full bg-transparent text-[20px] sm:text-[24px] text-foreground placeholder:text-text-dim tracking-tight pr-10"
/>
{query && (
<button
type="button"
onClick={() => {
setQuery("");
inputRef.current?.focus();
}}
className="absolute right-0 top-1/2 -translate-y-1/2 p-2 text-text-dim hover:text-accent transition-colors"
aria-label="Clear query"
>
×
</button>
)}
</div>
{query.trim() && (
<div className="flex items-center gap-2">
<button
onClick={() => handleSubmit()}
disabled={loading}
aria-label={loading ? "Fetching results..." : "Fetch results"}
className="bg-accent text-background px-4 py-2 text-[13px] font-bold hover:bg-[#00cc33] disabled:opacity-50 min-w-[60px] min-h-[44px]"
>
{loading ? "..." : "Fetch"}
</button>
<button
onClick={() => {
setQuery("");
setResult("");
setError("");
setProviderStatus(null);
setResolveTime(null);
setSourceProvider(null);
setQualityScore(null);
setParsedResults([]);
setViewRaw(false);
inputRef.current?.focus();
}}
aria-label="Clear input and results"
className="bg-transparent text-text-dim px-4 py-2 text-[13px] border-2 border-border-muted hover:border-accent hover:text-accent min-h-[44px]"
>
Clear
</button>
</div>
)}
</div>
{query.trim() && (
<div className="text-[11px] text-text-muted mt-2 uppercase tracking-wider">
{isUrl ? "Resolving as URL" : "Searching"}
</div>
)}
{providerStatus && (
<div
role="status"
aria-live="polite"
className="text-[11px] text-accent mt-2 animate-pulse"
>
{providerStatus}
</div>
)}
</div>
<SearchSection
query={query}
setQuery={setQuery}
handleSubmit={handleSubmit}
loading={loading}
inputRef={inputRef}
error={error}
hasResult={Boolean(result)}
onClear={handleClear}
providerStatus={providerStatus}
isUrl={isUrl}
/>

{/* Error */}
{error && (
<div id="search-error" role="alert" className="p-4 border-b-2 border-border-muted text-error text-[13px]">
{error}
</div>
)}

{/* Output */}
<div className="flex-1 flex flex-col min-h-0">
{result ? (
<>
{/* Metadata bar */}
<div className="flex items-center justify-between flex-wrap gap-3 px-4 py-2 border-b-2 border-border-muted text-[11px] text-text-muted">
<div className="flex items-center gap-4 flex-wrap">
<span>
Source: <span className="text-accent">{sourceProvider}</span>
</span>
{resolveTime && <span>{resolveTime}ms</span>}
<span>{charCount.toLocaleString()} chars</span>
{qualityScore !== null && (
<span title="Quality score (0-100)">
Quality: <span className={qualityScore >= 70 ? "text-accent" : qualityScore >= 40 ? "text-[#ffaa00]" : "text-error"}>{qualityScore}</span>
</span>
)}
</div>
<div className="flex items-center gap-2">
<button
onClick={() => setViewRaw(false)}
className={`px-3 py-1 border border-border-muted ${!viewRaw ? "text-accent border-accent" : "text-text-muted"}`}
aria-pressed={!viewRaw}
>
Cards
</button>
<button
onClick={() => setViewRaw(true)}
className={`px-3 py-1 border border-border-muted ${viewRaw ? "text-accent border-accent" : "text-text-muted"}`}
aria-pressed={viewRaw}
>
Raw
</button>
<button
onClick={handleCopyResult}
aria-label={copied ? "Copied to clipboard" : "Copy to clipboard"}
aria-live="polite"
className="hover:text-foreground transition-colors min-h-[36px] px-2"
>
{copied ? "Copied" : "Copy"}
</button>
</div>
</div>
<MetadataBar
sourceProvider={sourceProvider}
resolveTime={resolveTime}
charCount={result.length}
qualityScore={qualityScore}
viewRaw={viewRaw}
setViewRaw={setViewRaw}
handleCopyResult={handleCopyResult}
copied={copied}
/>
{viewRaw || parsedResults.length === 0 ? (
<textarea
readOnly
Expand Down Expand Up @@ -249,4 +147,6 @@
</div>
</div>
);
}
};

export default MainContent;
Loading
Loading