Replace integer-only 1-5 stars with 0-5 scale in 0.5 increments.
Each star has left/right click zones for half/full values, plus a
dedicated 0-star button. Backend validation updated to accept the
new range. Half-filled stars rendered via CSS linear-gradient on
background-clip: text.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Position root node near left edge of viewport so the tree has room
to expand rightward when categories are opened.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove <hr/> from header flex container that broke title centering
- Apply viewport-fit zoom logic on all screen sizes, not just mobile
- Set SVG height to calc(100vh - 120px) so tree fills available space
- Add overflow:hidden to body to prevent scrolling past viewport
- Remove unnecessary top:20px offset from header
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move theme toggle from legend to header as "Light Mode"/"Dark Mode"
button visible on both desktop and mobile. Add "Notes" button in
header (desktop only; mobile keeps floating pill). Legend and notes
content now load into a 400px slide-in side panel on desktop, matching
the full-screen mobile overlay. Escape key closes notes panel.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adjust the SVG viewBox on mobile to match the actual rendered aspect
ratio instead of the fixed 1280x800 landscape ratio. The previous
implementation computed the zoom scale against the viewBox dimensions,
but on a tall/narrow mobile screen with min-height: 60vh the SVG
element is much taller than the viewBox maps to with xMidYMid meet.
By stretching the viewBox to match the real element proportions, the
tree's vertical extent now has proper headroom and the initial zoom
fills the viewport as expected.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
On screens ≤768px, pre-compute the bounding box of the collapsed
tree layout and apply a zoom transform so the root + top-level
categories fill the viewport immediately, instead of starting tiny
in the center.
Also adds a reusable zoomToFill() utility for future reset-view use.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
On mobile (<=768px), the legend, notes, and about sections consumed most
of the viewport, leaving the D3 tree nearly unusable. This moves the
theme toggle into the header as a compact "Light Mode"/"Dark Mode"
button, hides the below-tree content, and adds a floating "Notes"
button that opens all that content in a full-screen overlay panel
(matching the existing tool detail panel pattern). Desktop layout
is completely unchanged.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The panel overlay (z-index 199) was intercepting all clicks on the tree
when the panel was open, forcing users to close the panel before selecting
a different tool. Setting pointer-events: none on the overlay lets clicks
pass through to tree nodes, where openPanel() already handles replacing
the panel content. The SVG background click handler still closes the panel
when clicking empty canvas.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds a region badge that always shows in the tool details panel.
Defaults to "Global" when the region field is absent or set to "global".
Accepts a two-letter country code string (uppercased) or an array of
country codes (joined as "US, UK"). Badge is styled green (#2e7d32).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Show boolean feature badges in the tool details panel when true:
- API (blue): tool has a public API
- Invite Only (amber): tool requires an invitation
- Deprecated (dark red): tool is no longer actively maintained
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move Open Tool button to top (below breadcrumb), add HR dividers,
split Input→Output into its own section separate from Usage Context,
and rename Rating label to Community Rating.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Backend (src/worker.js):
- Add POST /api/vote: upvote/downvote with toggle behavior, one vote per
session per tool, returns net score
- Add POST /api/report: flag tools as dead_link/paywalled/incorrect_info;
auto-creates GitHub issue via GitHub API when REPORT_THRESHOLD (3) unique
reports of the same type are received (requires GITHUB_TOKEN secret)
- Update GET /api/tool-stats to include votes { up, down, score }
- Extract validateCommon() and isRateLimited() helpers to reduce duplication
Frontend (public/js/arf.js):
- Replace static rating placeholder with _renderVoteUI(): renders thumbs
up/down buttons, reads cached vote from sessionStorage, fetches live score
async from /api/tool-stats
- Add _castVote(): fires POST /api/vote, updates button active state and
score display, persists user vote in sessionStorage
- Add _resetReportButtons() and _submitReport(): wire report buttons per
panel open, POST /api/report, show inline feedback message
HTML (public/index.html):
- Replace static rating span with vote row (thumbs up, score, thumbs down)
- Replace static GitHub report link with three report buttons + feedback div
CSS (public/css/panel.css):
- Add .vote-btn, .vote-score (.positive/.negative/.zero) styles
- Add .report-btn, .panel-report-feedback styles
- Remove old #panel-report-link styles (replaced by button row)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- src/worker.js: Cloudflare Worker entry point handling POST /api/track
and GET /api/tool-stats, falling through to ASSETS for static files
- wrangler.jsonc: wire main entry point + CLICK_DATA KV binding
(namespace: osint_framework_kv, id: cdb975e31e)
- public/js/arf.js: fire sendBeacon to /api/track on Open Tool click
Privacy: no IPs stored, no cookies, client-generated session_hash only.
Dedup: one counted click per session per tool (1-hour TTL).
Rate limit: 60 req/min per session_hash (2-min TTL key).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Status (live/down/degraded/unknown) and pricing (free/freemium/paid/unknown)
pill badges always shown in panel badges row with semantic colors
- OPSEC section now renders colored badge (passive/active/unknown) + opsecNote text
- Description section always visible; shows italic placeholder when no data
- Usage context renders bestFor and input→output mapping; hidden when all empty
- CTA button label now shows "Open [Tool Name] ↗" with actual tool name
- Added badge pill CSS (.badge-live, .badge-down, etc.) to panel.css
- Seeded Shodan (Discovery subcategory) with all new enrichment fields as demo
Co-Authored-By: Paperclip <noreply@paperclip.ing>
zoomToNode was using svgH/2 (y=400 in viewBox space) as the pan target.
Because the SVG sits below the header, the true viewport center is at
(window.innerHeight/2 - rect.top) / svgScale
not svgH/2. The discrepancy equals roughly the header height, which is
why the zoomed node consistently appeared too low.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Clicking a search result now collapses the full tree, expands all
ancestor branches of the matched node, highlights the node with an
accent-colored circle and bold text, and pans the viewport to it.
A small ↗ icon remains in each result for users who want to open the
tool's website directly.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
zoomToNode(d) was called after every expand/collapse, causing jarring
viewport jumps as the tree layout reflows node positions. Removing it
lets users pan and zoom freely without unexpected repositioning.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
D3 migration to v7 is complete. The old vendored v3 file is no longer
referenced anywhere — index.html loads js/d3.min.js which is copied
from node_modules by the postinstall script. CI smoke test already
validates the correct filename.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Both zoomToNode() (pan/zoom, THE-79) and the search/badge additions from
master (THE-66, THE-73) are preserved. No logic changes to either side.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Apply d3.zoom() to SVG container for mouse wheel zoom and drag-to-pan
- Implement zoomToNode() that auto-centers viewport on clicked/expanded node
- Store svgEl reference separately from vis group for zoom target
- Add grab/grabbing cursor CSS on SVG to communicate interactivity
- Preserve compatibility with dark mode, viewBox/responsive SVG, null-child safety
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Parse (T)/(D)/(R)/(M) suffixes from tool names and render them as
colored tspan elements in the SVG tree. Add matching badge pill styles
to the HTML legend so it visually matches the tree indicators.
- T (tool) → blue #4a9eda
- D (dork) → orange #e8944a
- R (registration) → amber #c8a030
- M (manual URL) → purple #9a7eda
Badge colors are theme-invariant; they work on both dark and light
backgrounds without separate light-mode overrides.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds a debounced text search input above the D3 tree that filters all
~1,000+ tool nodes by name and description. Results appear as a flat
list with breadcrumb paths; clearing the input hides the panel.
- No build step required: vanilla JS, no new dependencies
- Sanitizes HTML output (escapeHtml/escapeAttr) and validates URLs (http/https only)
- Dark and light mode compatible via existing CSS variables
- Results capped at 50 to keep rendering fast
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Closes THE-34, THE-35, THE-36.
- Replaces all hardcoded colors in arf.css with CSS custom properties
- Dark palette defined on :root (default); light palette on body.light-mode
- Removes old .dark-Mode class; D3 inline styles now read CSS vars at render time
- goDark() toggles light-mode class and persists preference to localStorage
- Flash-prevention inline script in body applies stored theme before first paint
- Toggle button label reflects current theme state
Co-Authored-By: Paperclip <noreply@paperclip.ing>
d3.hierarchy() in D3 v7 does not tolerate null entries in children
arrays (v3 was lenient). A null element at Username.children[1] caused
the entire tree to fail silently.
- Remove the null entry from arf.json
- Add a defensive children filter in d3.hierarchy() call so future
null entries degrade gracefully instead of breaking the tree
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace d3.layout.tree() with d3.tree()
- Replace d3.svg.diagonal() with d3.linkHorizontal() (x/y accessors swap coords)
- Replace d3.json(url, cb) with d3.json(url).then(cb)
- Replace d3.hierarchy() for root node; use .descendants() and .links()
- Strip svg: namespace prefixes from append/insert calls
- Update xlink:href to href on anchor elements
- Update data accessors to use d.data.name, d.data.url, etc.
- Fix .style("fill") call (was malformed in v3 code)
- Update event handler signature to (event, d) for D3 v7
- Use node.merge(nodeEnter) and link.merge(linkEnter) for v5+ update pattern
- Add public/js/d3.min.js (v7.9.0) copied from node_modules
- Update index.html to load d3.min.js instead of d3.v3.min.js
- Update CI smoke test to check for d3.min.js
- Keep d3.v3.min.js in place until visual parity confirmed
Co-Authored-By: Paperclip <noreply@paperclip.ing>