What's new in RDTSearch CLI, release by release.
✨ Enhanced animations
- Global easing:
ease(f) = 1 − (1−f)³ applied to all animate_view frames — counters, bars, and sparklines slow down as they arrive instead of a linear progression.
- Generic reveal curtain
animate_reveal(state, lines): views without an f builder (search results, /domain, /status, /suggest, /settings, /doctor, /help) appear top to bottom; lines that just arrived are first dimmed (DIM on ANSI-less text) then colored on the next frame. frame_fits(n) keeps the curtain out of append mode.
- View pill flash: during animation frames, the header pill is inverted (light background, view-colored text) then returns to its normal style — visually signals the view change.
- Shake on error: an
err notice in in-place mode → the whole frame shakes by one column (2 round trips, ~160ms) during the bold flash, via state["shake"] and a width reduced by one column (same-length lines → no residue).
/compare winner shown last: the ★ Winner line only appears at f = 1, then flashes in reverse video for ~120ms.
- Live
/watch sparkline: the view accumulates page readings (60 max, rebased on the min) and plots the live evolution under "Last pass".
✨ /settings: 4 new settings + improved menus
theme (default/ocean/high-contrast): three full palettes VIEW_BG/VIEW_FG (pills + title markers + prompt), applied live by apply_theme(), persisted. "High contrast" = vivid colors on saturated backgrounds (accessibility).
stats_days (1–365, default 7): default period for /stats AND the stats CLI command without -d.
sidebar (on/off): hides the Statistics panel even in a wide terminal; the header then shows latency + indexed pages (fmt_short: "4.74M pages"), also in narrow terminals ≥ 60 columns.
bell (off by default): \a when /watch (TUI and CLI) detects a change.
- Menus: the current value is marked ✓ and preselected (
initial= of menu_select); after confirming you return to the settings menu (chaining, Esc to exit); numbers 1.–13. aligned to 2 digits.
- Keyboard filter in all menus: typing letters live-filters the list (palette, completion, settings…), Backspace clears the filter, digits jump directly as long as no filter is active. Typed text shows in cyan after the menu header.
✨ Commands
/help <command> (aliases accepted: /help bl): a focused help sheet — description, aliases, examples (HELP_EX). /help alone is unchanged.
c <n>: copies a result's URL to the clipboard without opening the browser (clip on Windows in UTF-16, pbcopy on macOS, xclip/xsel/wl-copy on Linux — a clear message if none exist). Results footer + help updated (intercepted shortcuts: n, p, q, i, c, digits).
/backlinks history <domain> in the TUI: evolution sparkline + first→last snapshot delta + a table (last 12) with bars — the endpoint was previously CLI-only.
/domain: "Index share" — % of the domain's pages in the total index (from SESSION["engine"] data, zero extra calls), decimal comma in French, "< 0.01%" floor.
/stats: trend — Δ% of the displayed window vs. the previous one (↑ +12% vs previous period, green/red, 0.5% threshold).
- Domain completion: Tab after
/domain , /backlinks , /status , /watch , /compare → a "Recent domains" menu (last_domain + history + cache snapshots, 12 max, filtered by the typed prefix, already-entered domains excluded — useful for /compare's second domain).
- Palette with aliases: a
/d, /bl, /vs… column shown between the command and its description.
/domain footer also suggests /watch <domain>; results footer suggests c <n>.
🔧 Internal
ALIAS_MAP/REV_ALIAS replace the manual ALIAS_HEADS list (now derived); run_tui normalizes head via ALIAS_MAP → dispatches on canonical names only.
handle_tab applies settings directly (apply_setting): the ("set", …) kind is gone from run_tui.
- New helpers:
ease(), frame_fits(), animate_reveal(), apply_theme() (+ THEME_BG/THEME_FG), fmt_short(), known_domains(), copy_to_clipboard()/copy_result(), bl_history_lines(), help_cmd_lines()/HELP_EX, tui_help_cmd(); header_line(w, view, flash); draw_frame reads state["shake"]/state["pill_flash"]; import subprocess.
- ~32
TR_EN strings added, 4 keys reworded (intercepted shortcuts, settings/stats help, unknown setting).
- Verified: compiles +
ast.parse(feature_version=(3, 8)), AST extraction of T() keys → 0 missing (COMMAND_DESC and HELP_EX included), piped TUI walkthrough in FR and EN (574/577 frame lines aligned to 120 columns), smoke test of all 17 f-builders at f ∈ {0, 0.25, 0.5, 0.75, 1} (1,419 lines), live CLI (search, domain, stats, suggest, health, status, doctor, compare, backlinks ×5, settings set/get/--json/reset + validation errors against a temp APPDATA), stats without -d correctly follows stats_days.
✨ Vloki, the spider companion (2026-07-17)
- Home-screen mascot drawn by Théo: a 10-line pixel-art spider (a nod to spider = the RDTvlokipBot crawler) hanging from its
░ thread in the empty space to the right of the home screen, named Vloki (name auto-centered under the drawing).
- Animated wave (also drawn by Théo): Vloki raises a leg and waves — 11 frames (rise F1→F6, then a mirrored descent, 0.09s/frame ≈ 1s). Played on launch once the engine has loaded, then every 10 idle seconds on the home screen (empty input line). Keyboard detection doesn't swallow any keys:
msvcrt.kbhit() on Windows, select on Unix (key_waiting()) — the first keystroke interrupts the wave and lands normally in the input line.
- Speech bubble: a time-of-day greeting ("Bonjour !" 5am–6pm, "Bonsoir !" otherwise) + a random tip from 10 (
TIPS), different on each launch and each /clear (never the same one twice in a row). Tips are translated (12 TR_EN keys).
- Cleanly integrated: colored by the active theme (
VIEW_FG["home"]), an ANSI-aware right-aligned merge into the home screen's lines (vpad/vlen), constant width during the animation (frames padded to SPIDER_W → zero jitter). If the full version doesn't fit (terminal < ~170 columns), automatic fallback to a mini spider (5 lines); in ASCII glyph mode, a # mini version; too narrow → hidden. No line overflows the frame, and the animation is skipped outside a TTY/colors/in-place mode like the other animations.
- Internal:
SPIDER_FRAMES/SPIDER_W/SPIDER_MINI/SPIDER_ASCII, TIPS, pick_tip(), companion_block(), companion_wave(), key_waiting(), SESSION["tip"], an idle hook in line_input, random + textwrap imports.
🐛 Fixes (2026-07-17, pre-release)
- The theme is finally visible. Previously, switching themes only touched the view pill and the
▌ title marker — and in the settings view (where you change the theme), those colors were near-identical grays across all three themes, giving the impression that "nothing changed." The theme now also drives the header bar background (THEME_BAR: 236 / 17 midnight blue / 232 black), the frame border color (THEME_FRAME: gray / 31 ocean blue / 255 white), and the logo gradient (THEME_GRADIENT: cyan→blue for ocean, yellow→red for high-contrast), applied live by apply_theme() (mutating the THEME dict and GRADIENT[:] in place). The "settings" colors of the ocean (pill 24, marker 45) and high-contrast (purple pill 55) palettes are now distinct from the default: the effect is immediate as soon as the menu is confirmed.
- Tab after
/command arguments no longer shows search results. The "/ with a space" branch of handle_tab fell through to contextual menus when the command didn't expect a domain: after a search, /help + Tab (or any /command args + Tab) opened the found-pages menu. Fixed: the branch now ends with return None. Bonus: /help + Tab now suggests commands to document (a "Help for a command" menu with descriptions, direct completion if the prefix is unique — /help dom + Tab → /help domain).
- Tab now accepts the grey ghost suggestion. Typing
rdt after searching rdtvlokip correctly showed the vlokip ghost, but Tab opened contextual menus instead of completing (→ alone worked). Tab now accepts the displayed ghost (cursor at end of line), just like →. Intentional exception: /command without a space keeps classic command completion (which adds a space for the argument). Help text updated: "→ or Tab (end of line)".
- Immediate feedback in the chained settings menu. Confirming a setting via Tab (theme, sidebar…) now redraws the frame right away (
draw_frame after each apply_setting, in in-place mode with colors): the theme change is visible immediately, and the settings list behind the menu stays up to date. Previously, the effect only appeared after leaving the menu with Esc.
- The results menu on Tab now follows
digit_mode. The results branch of handle_tab now requires digit_mode == "open" (consistent with digits and the footer): after /help or /domain, Tab on an empty line reopens the command palette instead of a past search's results.
- 2
TR_EN keys added ("Help for a command", "pick the command to document"), 1 renamed ("→ or Tab (end of line)").
- Verified: compiles + AST 3.8, 0 missing
T() keys, 18 unit tests on Windows (themes ×3, handle_tab ×5, line_input ghost/completion via simulated read_key), 120-column piped TUI with theme changes → frame 100% aligned, live CLI (version, health, settings theme set/get/reset) against a temp APPDATA.
✨ TUI animations (new animations setting, on by default)
animate_view(state, build, steps, delay) framework: redraws the frame 3–5 times with an increasing factor f (~200ms total), then settles on the final frame. Automatically skipped outside a TTY, without colors, in append mode, or via the setting. Builders accept an optional f=1.0 — the final render is strictly identical to before.
- Counting-up counters: home screen totals,
/stats, /backlinks (pages, domains, links…) count up from 0 to their value.
- Filling bars:
/stats daily bars, /backlinks dofollow/internal ratios and top domains, anchors, /health latency gauge, i <n> score bars.
- Sparklines drawn left to right (hourly crawl, session latency).
- Waving home logo: the gradient sweeps across the logo on startup and on
/clear.
/watch pulse: the "active monitoring" dot breathes (●/○) while waiting; the sidebar (session duration) refreshes as it goes.
- Notice flash: ok/warn/err notices appear bold for ~120ms then return to their normal style.
- Enhanced spinner: elapsed time shown from 0.5s onward (
Searching… 1.2s) and n/total steps on multi-request commands (/backlinks 1/2–2/2, /compare 1/4–4/4).
✨ CLI settings (persistent, unlike --lang/--timeout)
rdtsearch settings: shows the numbered settings + the terminal diagnostic; --json includes the file path and active values.
rdtsearch settings <key> <value>: changes and saves to config.json (lang, glyphs, colors, hide_adult (alias adult), history, timeout, animations; booleans as on/off). parse_setting() shared between CLI/TUI, explicit errors (possible values, bounds). The config is reloaded from the file before writing so session overrides (--lang/--timeout) aren't persisted.
rdtsearch settings <key>: shows just the value. settings reset: deletes config.json + history. settings clear-cache: purges backlink snapshots.
✨ Redesigned /settings view
- Numbered settings: typing
1–9 directly opens that setting's menu (new digit_mode="settings"); outside a terminal, a clear message pointing to /settings key value.
/settings key value inline (same keys as the CLI), plus /settings reset, /settings clear-cache, and /lang fr|en|auto as a shortcut.
- Live preview: in the Colors and Glyphs menus, the hovered value applies immediately (menu included), restored on Esc — via the new
on_move callback of menu_select.
- Yellow ● dot on modified settings (≠ default, compared against
DEFAULTS) + legend; the timeout's "(default)" label is replaced by this system.
- 2 new actions in the Tab menu: Reset (defaults + deletes
config.json/history) and Clear cache (shows snapshot count).
- New Animations setting (Enabled/Disabled).
✨ Various TUI improvements
i in the results Tab menu opens the detail sheet for the highlighted result (Enter = browser, as before) — new alt parameter of menu_select.
- Duel bars in
/compare: under Indexed pages and Inbound links, a two-color cyan/magenta bar shows the balance of power.
- Latency sparkline in
/health: session latency history (SESSION["lat_hist"], 40 max) + min/max.
- Backlinks quota in the sidebar: an
Analyses n/3 line (red when exhausted) as soon as /backlinks has loaded the graph.
- More readable 429s: the block's end time is added to the message (
… retry in 42s (at 14:32:05)), computed from Retry-After (ignored if non-numeric).
- Help text updated (
/settings lang fr, setting number, i in the results menu); settings footer 1-9 or Tab; ~35 TR_EN strings added.
🔧 Internal
DEFAULTS (source dict of defaults) + CONFIG = dict(DEFAULTS); reset_config(), clear_cache(), retry_at(), note_latency(), settings_cats(), setting_shown(), choose_setting_value(), apply_setting() (replaces run_tui's inline set block), open_setting(), parse_setting(), cmd_settings(), duel_bar(), anim_ready(), animate_view().
- Verified: compiles +
ast.parse(feature_version=(3, 8)), full piped TUI walkthrough, 367 frame lines aligned to 120 columns (ANSI stripped), smoke test of builders at intermediate f, live CLI (search, domain, stats, suggest, health, status, backlinks ×6, compare, settings set/get/reset/clear-cache/--json — mutations tested against a temp APPDATA).
📦 Metadata fix
- Fixed documentation URL:
https://search.rdtvlokip.fr/api → https://search.rdtvlokip.fr/api-docs (README.md + [project.urls]). No code changes; the API base used for calls (BASE_URL + /api/… endpoints) is unchanged.
- Published on PyPI.
📦 Publishing fix
- Republished as 0.13.1. 0.13.0 had gone live without the
RDTSearch distribution name, then was deleted from PyPI. PyPI forbids reusing a deleted release's filename → 0.13.0 is permanently burned, hence the bump to 0.13.1. No code changes; only metadata was fixed: name = "RDTSearch", Homepage https://rdtsearch.rdtvlokip.fr/, PyPI + GitHub links.
🚀 First PyPI release
- Published on PyPI:
pip install rdtsearch. Distribution name RDTSearch (normalized to rdtsearch by PyPI — both forms install). Command and module unchanged (rdtsearch).
- Links added (
README.md + [project.urls]): PyPI, GitHub repo.
✨ 4 new SEO & reliability features
/compare a.fr b.fr (alias /vs) + CLI compare: an SEO duel between two domains side by side — indexed pages, inbound links, last crawl pass, #1 anchor, winner (score n–n) with ★ on the winning values. New "duel" TUI view (magenta pill 125/213). Data: bot/domain + anchors — ⚠️ anchors.total is the graph's true total (verified live: 22,542 for github.com), whereas history snapshots reflect the analysis truncated to 10 (backlink_count: 10) → never use those as totals.
- Local cache for backlink analyses (
%APPDATA%\rdtsearch\cache\bl_<domain>.json): /backlinks <domain> and backlinks detail reuse today's snapshot (zero calls, preserving the 3/day quota); note "Local snapshot from … — --fresh to re-query"; --fresh forces the API (TUI and CLI); if the API fails (429/outage), falls back to the last snapshot even if stale, with the server error shown.
watch <domain>: continuous indexing monitoring, in both CLI and TUI. CLI watch <domain> [-i sec] (default 300s, clamped 60–3600): a line per change, in-place status, flush=True, Ctrl+C = summary. TUI /watch <domain> [sec] (default 30s): a live frame redrawn each interval with a timestamped change log (+N pages in green / bot pass); non-blocking wait via wait_or_key() (msvcrt on Windows, select on Unix) → any key stops it; in piped/non-TTY mode, falls back to a single snapshot. Loop verified with a unit test (simulated TTY + keypress).
doctor [--json] (CLI and TUI /doctor): self-diagnostic — version/Python/OS, terminal (TTY, encoding, glyphs, colors), configuration (file, language, timeout, history, snapshots), server (health ✓/✗, latency, clock skew, tier + backlinks quota daily_used/daily_limit). CLI: exit code 0/1 based on server health; doctor_report() shared, doctor_lines() for the "diagnostic" view.
- Commands now sorted automatically:
COMMANDS = sorted(COMMAND_DESC) (previously: a duplicated manual list). A command added to COMMAND_DESC now appears on its own, in alphabetical order, in the palette and completion.
/compare, /watch, /doctor added to the palette, help, contextual footer, and colored views (duel 125, monitoring 23, diagnostic 53); /vs alias; ~45 TR_EN strings added.
✨ Full settings (/settings)
- 6 settings persisted in
config.json (previously: language only): lang, glyphs (auto/utf/ascii), colors (auto/on/off), hide_adult (bool), history (bool), timeout (1–120s, default 15). Strict validation on load (bounds, bool rejected for timeout).
--timeout S as a global CLI option (session only, clamped 1–120), same logic as --lang. In the Tab menu: choice of 5 / 15 / 30 / 60s.
- 2-level Tab menu in the settings view: pick the setting → pick the value (same
menu_select mechanics as the language menu).
- Forceable glyphs:
apply_glyphs() rebuilds S{} and UTF live (utf_auto() + glyph_set(utf)). A user on old CMD can force UTF on, or the reverse.
- Colors persisted and toggleable live:
C_ANSI keeps the original codes, apply_colors() restores or clears C. CLI --no-color and non-TTY auto-disable still take priority (NO_COLOR["cli"]).
- Adult content filter:
adult_filter() strips is_adult results (TUI + CLI), gray note "{n} adult result(s) hidden". TUI numbering stays aligned with the filtered state["results"]. --json stays unfiltered (raw).
- Opt-in persistent history (default: off, privacy-first):
%APPDATA%\rdtsearch\history.txt, 200 entries max, saved on every input. Disabling it = file deleted.
- Diagnostic in the settings view: terminal size + render mode (in-place/append), stdout encoding, active glyphs (UTF/ASCII), active colors, detected OS language.
- ~35 new translated strings (
TR_EN).
🐛 Fixed (reported by Théo along the way)
- "Incomplete" command palette:
menu_select was hardcoded to 8 visible lines (scrolling existed but nothing indicated it → the list looked truncated). Height is now dynamic (LINES − 6, min 4): all 14 commands show at once on a normal terminal; an n/total counter appears in the header when it still scrolls.
- Frame overflow in ASCII mode:
vcrop reserved 1 column for the ellipsis, but ASCII ... is 3 characters → lines ran 2 columns too long (invisible as long as UTF stayed active). Now reserves len(S["ell"]). Verified: 185 frame lines at identical width across a full walkthrough.
- BOM in piped mode: on PowerShell 5.1,
"/settings" | rdtsearch --tui arrived as /settings (UTF-8 BOM mangled as cp1252) → treated as a search. The piped fallback now strips the BOM (both U+FEFF and cp1252 forms).
🐛 Fixed (reported by Théo)
? instead of glyphs (❯, ▇▃▁, ╭…) in classic CMD. Cause: Python ≥ 3.6 always reports utf-8 on a real Windows console (PEP 528), so UTF=True, but old conhost's font can't render those characters. Rule added: on Windows, UTF glyphs only activate in a modern terminal (detected via WT_SESSION, WT_PROFILE_ID, TERM_PROGRAM, ConEmuANSI, ANSICON, or TERM); CMD/PowerShell in conhost → full ASCII fallback for S{} (>, #, +--+, ASCII logo). Windows Terminal, VS Code, ConEmu/Cmder keep the full UTF interface.
📦 Packaging
pyproject.toml added: pip install -e . installs the rdtsearch command (entry point rdtsearch:main). Zero impact on the single-file distribution; paves the way for PyPI.
- PyPI-ready:
README.md (English, PyPI page), AGPL-3.0-or-later license (LICENSE + PEP 639 metadata), wheel + sdist built (python -m build), twine check PASSED, install tested in a clean venv. rdtsearch name confirmed available on PyPI.
🌍 Internationalization — /settings + French/English
- New
/settings command (alias /lang): a settings view showing the current language and the config file path; Tab → Auto / Français / English menu (same mechanics as the period menu — menus can only open while typing)
- Automatic OS language detection by default:
GetUserDefaultUILanguage (ctypes, Windows registry) → French if LANGID 0x0C, English otherwise; on Unix LC_ALL/LC_MESSAGES/LANG
- Persistence:
%APPDATA%\rdtsearch\config.json (Windows) / ~/.config/rdtsearch/config.json (Unix), values auto / fr / en
--lang auto|fr|en as a CLI flag (session only, not persisted); pre-scans sys.argv so --help already prints in the right language
- The whole interface now goes through
T() (~260 strings): TUI (views, menus, footer, help, home screen, notices, spinners), API errors, CLI output, argparse help. TR_EN dictionary keyed by the French string. Language switching is instant (header/sidebar/footer on the next render)
- ⚠️ Old "100% French" rule replaced: French by default (or OS language), English optional — Théo's call for the GitHub/PyPI release
🐛 Fixed (UX — reported by Théo)
- Ghost text inconsistent with the Tab menu: typing
/h showed the ghost elp (the line looked like a complete /help), then Tab opened a "/help or /health?" menu — the line and the menu contradicted each other. Rule fixed: command ghost text only shows when completion is unambiguous; multiple candidates → no ghost (and no history fallback), Tab shows the menu. History fallback stays active for free text and commands with an argument (/domain r → dtvlokip.fr).
🐛 Fixed
ValueError crash on the ² key (AZERTY, just above Tab) in arrow menus and direct input. Cause: '²'.isdigit() returns True in Python (Unicode superscripts) but int('²') raises an exception. All 3 call sites (menu_select, direct digits, i <n>) now go through a new strict-ASCII is_digits(); typing ² alone now just becomes a search.
✨ Next-gen input (TUI)
- Command palette: Tab on an empty line (outside contextual menus) → an arrow menu of all commands with their description. Commands with an argument (
/domain…) get pre-filled; otherwise run directly
- Ghost completion (fish-style): the likely continuation shows in gray after the cursor (commands, then history); → at end of line accepts it
- Live coloring: the typed
/command turns green (valid or a valid prefix, aliases included) or red (unknown)
🔬 Result detail sheet: i 3
- Final score, BERT class, query intent, word count, full tracker list, full description
- Score breakdown: bars for additive points (bm25, title, domain_match, url_match…),
×1.05 multipliers in green (boost) / red (penalty), grouped 4 per line
- Digits still open the browser from the detail sheet
📊 Enriched stats (zero extra API calls — data already received)
- Hourly crawl: sparkline of the last 24h + total + peak with its hour (
hourly_values/hourly_labels, never used before)
- New domains/day: a
+N dom. column to the right of the daily bars (domains_daily)
🔎 API discoveries (verified live)
score_breakdown = a dict of 23 floats: additive points (bm25 ~1700, title, domain_match, url_match, desc) + multipliers around ×1 (https, depth, freshness, pagerank, bert_boost, short_query_boost…)
- The API also provides
display_url (scheme-less URL) and an id per result
🎨 Interface (TUI)
- Unified view titles: a
▌ marker colored per view (same hue as the header pill), applied to all views and help sections (VIEW_FG palette)
- Contextual prompt: the
❯ takes on the current view's color
- Colored footer: commands in cyan, descriptions in gray — much easier to scan; now cropped to terminal width (no more line wrapping)
- Enriched home screen: total pages/domains shown under the logo as soon as it loads + a "Try:" block with 4 ready-to-type examples
- Stats: dates in DD/MM/YYYY format, bars smoothed to ⅛-block resolution (
▏▎▍▌▋▊▉█), a summary line (total · daily average · peak), peak day highlighted in green
- Backlinks (global view): dofollow/nofollow and internal/external distribution gauges (green/gray + percentage), top domains with proportional mini-bars
- Health: a colored visual latency gauge (green < 300ms, yellow < 1s, red beyond)
- Domain: new Status line with
● indexed / ○ not indexed
- URLs shortened for display:
https:// stripped, trailing slash cleaned (results + backlinks) — the full URL is still used when opening
- Notices: bold icon; header pill now bold
✨ Added
- "Did you mean /stats?": on an unknown command, suggests the closest match (
difflib, stdlib)
🐛 Fixed
- 429: the server message now takes priority. Discovered during testing:
/api/backlinks/detail has an anonymous daily quota (3 analyses/day) and its 429 carries a precise message ("Limit reached (3/day). Sign in for 10 analyses/day."). Previously the generic "retry in a minute" was shown (wrong for a daily quota). Order: server message (+ Retry-After if present) → Retry-After alone → generic.
🔧 Internal
- New helpers:
title(), hbar8(), ratio_bar(), fmt_date(), display_url(), hint(); edge (▌) and ring (○) glyphs with ASCII fallback
- Local
title variables renamed to t (they were shadowing the new module-level function)
✨ Added
- Tab in the stats view → an arrow menu to change the period: 1, 3, 7, 30, 180, 365 days, or the full history ("all" = no date bound)
- Instant period switching: re-rendered from the already-loaded cache, zero extra API calls (
/api/graph/data's limit is 10/min)
- Table title adapts: "(last 30 days)" / "(full history — N days)"
- Stats view footer: "Tab to change the period"; help text updated
- pty test harness: 3 new checks (period menu, 30 days, all) → 13/13 PASS
🐛 Fixed
- SyntaxError on Python < 3.12:
\uXXXX escapes had crept into f-string expressions (e.g. {'dom. r\u00e9f.':>9}). Backslashes are forbidden there before Python 3.12 (PEP 701). All 45 escapes in the file were converted to real UTF-8 characters; syntax validated with the parser in Python 3.8 mode.
✨ Added — Tier 3: backlinks (SEO)
- CLI
backlinks with 6 actions:
stats — global graph (126M links, 20.9M linked pages, 748K domains), dofollow/nofollow/internal/external breakdown, build date, tier shown
top [-n N] — most-linked domains (backlinks + referring domains)
detail <domain> — list of inbound links: source domain, URL, anchor ↳ "…", [nofollow] [sponsored] [ugc] badges
anchors <domain> — anchors with distribution bars █░ + percentages
history <domain> — a table of snapshots (links, dofollow, nofollow, referring domains)
export <domain> [--format csv|json] [-o file] — writes the file; anonymous tier → the exact server message ("premium accounts only"), not a crash
- TUI
/backlinks (alias /bl):
- without an argument → global view: graph stats + top 8 domains
- with a domain → inbound links + Top anchors with bars
- digits / Tab = open the source page in the browser (reusing the contextual mode)
api_get_text() for non-JSON responses (CSV export)
- API discovery: a tier system (
anonymous: 10 results max/request, export blocked, daily quota)
🎨 Interface
- Colored view pill in the header (different background per view: search, help, domain, stats, backlinks…)
- Smart contextual footers: after
/domain rdtvlokip.fr, the footer suggests /backlinks rdtvlokip.fr and /status rdtvlokip.fr already filled in; same between the stats/health/backlinks/submit views
- Sidebar: thin separators between sections + a new
Links line (graph total, loaded on startup)
- Arrow menus: a
❯ pointer on the selected line
- Home screen: keycap-style shortcuts
[Enter] [Tab] [/help] [q]
- Help: new SEO · backlinks section
✨ Added
- Privacy badges on every result (CLI + TUI):
● green (tracker_level 0), yellow (1), red (2)
- Text = list of detected trackers (e.g.
Google Analytics, Google Tag Manager, cropped to 44 chars) or a default label (no trackers / minimal tracking / trackers)
tracker_level null → no badge shown
[18+] badge in bold red when is_adult is true
- Badge legend in
/help
🔧 Changed
- Esc now clears the input line (and exits history recall)
✨ Added
- Raw keyboard input:
msvcrt (Windows) / termios + a manual UTF-8 decoder (Unix) — no more input()
- Tab completion for
/commands: a unique match → completed inline; multiple matches → an arrow menu
- Arrow menus
↑↓ + Enter (Esc cancels, digit = direct jump):
- multiple command matches
- suggestions → Enter runs the search
- results → Enter opens in the browser
- Input history
↑↓ (session-scoped, draft preserved)
- Full line editing:
←→ cursor movement, Home/End, Delete, Backspace, horizontal scrolling for long input
- A pty test harness (
pty_test.py) simulating Tab/arrows/Enter — 10 checks
📝 Notes
- Outside a terminal (pipes, scripts): automatic fallback to
input()
✨ Added
- Tier 2 API:
suggest, health, status (CLI) + /suggest, /health, /status (TUI)
- Clickable suggestions: typing the number runs the search
- Zero results → automatic suggestions "Try instead:" (via autocomplete, a silent call)
- Contextual digit mode: open a result or run a suggestion, depending on the view
- Contextual footer under the frame (changes per view)
- Full-bar header (dark background, magenta title, current view,
● status)
- Rounded corners
╭ ╮ ╰ ╯
- Colored latency: green < 300ms, yellow < 1s, red beyond
- Sidebar:
Today / Yesterday with a trend arrow ↑↓
health: status, latency, server time, clock skew
🔧 Changed
api_get now reads the JSON body of errors → the exact server message is surfaced
/status on a domain never submitted: the 404 is now a yellow info, not an error
- Unknown
status success schema → generic key/value display
🐛 Fixed
- Impossible to scroll up: the alternate screen buffer (
?1049h, vim/htop mode) disables scrollback by design (VS Code included) → removed
🔧 Changed
- Adaptive rendering: frame ≤ terminal height → in-place redraw (app-like feel); otherwise append mode → working native scrollback
- In append mode, notices now print without redrawing the frame (no history spam)
- Results now stay in the terminal after
/quit
✨ Added
- Full-screen console-style interface: title bar, main frame, Statistics sidebar
- Sidebar: engine status, actually-measured latency, global Pages/Domains/URLs, 14-day crawl sparkline, day-vs-peak bar, session (requests, duration)
- Rewritten help: "everything is a search by default" logic, concrete examples, a dedicated block for the
/q case
- ANSI-aware
vcrop/vpad/vlen: frame width accurate to the character
- Wide characters stripped (emoji/CJK) from frames — they were breaking the borders
- Colored notices (✓ ! ✗) shown inside the frame
- New
/refresh command
✨ Added
- Interactive interface (TUI): RDT logo in Unicode blocks with an animated gradient (ASCII fallback), live status on startup, threaded spinner during requests
- Opening results in the browser (type the number),
n/p pagination, forced /q <text>, /domain, /stats, /clear, /help, /quit
- Cursor hidden while loading, restored on exit; terminal window title set
- 429 errors shown without exiting the interface
🐛 Fixed
/api/graph/data's totals is cumulative over the days window (not global!) → now always fetched over 365 days + the array sliced. The real global totals finally show up (4.65M pages / 296K domains)
- Double HTML-escaped snippets (visible
é) → double html.unescape
✨ Added
- Initial CLI:
search (+ -p), domain, stats (+ -d), --json option everywhere
- Zero dependencies: urllib + argparse, Python 3.7+
- 429 handling with the
Retry-After header (exit code 2), clean network/timeout errors
- ANSI colors enabled on Windows (
os.system('')), auto-disabled outside a TTY
BrokenPipeError handled (pipes into head, findstr…)
- Input normalization: full URL → domain
- API
<mark> tags → bold yellow highlighting; protected console encoding (errors="replace")
- ASCII bars for the crawl history in
stats