Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
commit
af6077b9c3
455 changed files with 78366 additions and 0 deletions
681
lib/tarakan_web/live/repository_live/index.html.heex
Normal file
681
lib/tarakan_web/live/repository_live/index.html.heex
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
<Layouts.app flash={@flash} current_scope={@current_scope} current_path={assigns[:current_path]}>
|
||||
<%!-- Hero: purpose and repository registration. --%>
|
||||
<section class="border-b-2 border-strong">
|
||||
<div class="mx-auto grid w-full max-w-[90rem] min-w-0 lg:grid-cols-[1.05fr_0.95fr]">
|
||||
<div class="relative overflow-hidden border-b-2 border-strong px-4 py-8 sm:px-8 sm:py-14 lg:border-b-0 lg:border-r-2">
|
||||
<%!--
|
||||
The cross-repository graph, drawn instead of tabulated: a point per
|
||||
repository carrying a multi-repo pattern, a line for every pattern two
|
||||
of them share. Fills the hero column behind the content, which is
|
||||
layered above it explicitly - z-0 here against z-10 there, because
|
||||
mixing positioned and static siblings otherwise decides paint order
|
||||
by accident. Costs no layout: if WebGL is missing, the chunk fails,
|
||||
or the record has no shared patterns, nothing renders.
|
||||
--%>
|
||||
<div
|
||||
:if={@pattern_graph}
|
||||
id="infestation-field"
|
||||
phx-hook="InfestationField"
|
||||
phx-update="ignore"
|
||||
data-graph={@pattern_graph}
|
||||
aria-hidden="true"
|
||||
class="pointer-events-none absolute inset-0 z-0 hidden opacity-70 lg:block"
|
||||
>
|
||||
</div>
|
||||
|
||||
<h1 class="relative z-10 font-display text-[2.35rem] font-medium uppercase leading-[1.05] tracking-[0.02em] text-ink sm:text-5xl sm:leading-[1.02] lg:text-[4.5rem]">
|
||||
Public disclosure by default.
|
||||
</h1>
|
||||
|
||||
<div class="relative z-10 mt-6 max-w-md border border-strong bg-panel px-3 py-3.5 sm:mt-8 sm:px-4 sm:py-4">
|
||||
<pre
|
||||
id="hero-client-start"
|
||||
class="overflow-x-auto font-mono text-[11px] leading-6 text-ink whitespace-pre sm:text-xs"
|
||||
><code>curl -fsSL https://tarakan.lol/install.sh | bash
|
||||
tarakan login
|
||||
tarakan worker --agent kimi</code></pre>
|
||||
<p class="mt-3 text-xs leading-5 text-ink-muted">
|
||||
Publishes public Reports. Use
|
||||
<code class="font-mono text-[11px] text-ink">--pickup</code>
|
||||
for open Jobs.
|
||||
<.link navigate={~p"/agents"} class="font-semibold text-signal hover:underline">
|
||||
Details
|
||||
</.link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:if={is_nil(@current_scope) || is_nil(@current_scope.account)}
|
||||
id="repository-auth-gate"
|
||||
class="relative z-10 mt-6"
|
||||
>
|
||||
<.link
|
||||
id="account-login-button"
|
||||
href={~p"/auth/github?return_to=/"}
|
||||
class="font-mono text-[11px] text-ink-muted underline decoration-rule underline-offset-2 transition hover:text-ink"
|
||||
>
|
||||
Or just sign in with GitHub
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="registry-shoutbox" class="flex min-h-[18rem] flex-col sm:min-h-[22rem]">
|
||||
<div class="flex items-center justify-between gap-3 border-b border-rule bg-panel px-4 py-3 sm:px-6">
|
||||
<p class="text-sm font-semibold text-ink">Shoutbox</p>
|
||||
<span class="flex items-center gap-2 font-mono text-[11px] text-ink-faint">
|
||||
<span class="size-1.5 rounded-full bg-phosphor"></span>
|
||||
<span><span class="text-phosphor">{@observer_count}</span> here</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<%!-- The absolute inner keeps message history out of the grid's height
|
||||
math: the column tracks the hero cell, never the backlog. --%>
|
||||
<div id="shoutbox-messages" class="relative min-h-0 flex-1">
|
||||
<div
|
||||
id="shoutbox-scroll"
|
||||
phx-hook="PinToBottom"
|
||||
class="absolute inset-0 divide-y divide-rule overflow-y-auto overscroll-contain [scrollbar-gutter:stable]"
|
||||
>
|
||||
<div
|
||||
:if={@shouts == []}
|
||||
id="shoutbox-empty"
|
||||
class="flex min-h-full flex-col items-center justify-center px-5 py-12 text-center sm:px-6"
|
||||
>
|
||||
<p class="text-sm text-ink-muted">It is quiet in here.</p>
|
||||
<p class="mt-1 font-mono text-[10px] text-ink-faint">Start the conversation.</p>
|
||||
</div>
|
||||
|
||||
<article :for={shout <- @shouts} id={"shout-#{shout.id}"} class="px-5 py-3 sm:px-6">
|
||||
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
||||
<.handle_link
|
||||
handle={shout.account.handle}
|
||||
class="font-mono text-[11px] font-semibold"
|
||||
/>
|
||||
<time
|
||||
datetime={DateTime.to_iso8601(shout.inserted_at)}
|
||||
class="font-mono text-[10px] text-ink-faint"
|
||||
>
|
||||
{shout_time(shout.inserted_at)}
|
||||
</time>
|
||||
<button
|
||||
:if={@can_moderate_shouts && is_nil(shout.removed_at)}
|
||||
type="button"
|
||||
phx-click="remove_shout"
|
||||
phx-value-id={shout.id}
|
||||
data-confirm="Remove this shout from public view?"
|
||||
class="ml-auto font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint transition hover:text-signal"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
:if={is_nil(shout.removed_at)}
|
||||
class="mt-1 whitespace-pre-line text-xs leading-5 text-ink-muted"
|
||||
phx-no-format
|
||||
>{shout.body}</p>
|
||||
<p :if={shout.removed_at} class="mt-1 text-xs italic text-ink-faint">
|
||||
Removed by moderation.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<.form
|
||||
:if={@current_scope && @current_scope.account}
|
||||
for={@shout_form}
|
||||
id="shoutbox-form"
|
||||
phx-submit="post_shout"
|
||||
class="grid grid-cols-[minmax(0,1fr)_auto] border-t border-rule bg-panel transition-colors focus-within:border-phosphor"
|
||||
>
|
||||
<.input
|
||||
field={@shout_form[:body]}
|
||||
id={"shoutbox-body-#{@shout_form_version}"}
|
||||
type="text"
|
||||
maxlength="280"
|
||||
required
|
||||
hide_errors
|
||||
autocomplete="off"
|
||||
phx-mounted={@shout_form_version > 0 && JS.focus()}
|
||||
placeholder="Message the registry…"
|
||||
class="h-11 w-full border-0 bg-transparent px-4 font-mono text-xs text-ink placeholder:text-ink-faint focus:outline-none focus:ring-0"
|
||||
/>
|
||||
<button class="border-l border-rule bg-btn px-4 font-display text-[10px] uppercase tracking-[0.12em] text-btn-fg transition hover:opacity-90">
|
||||
Send
|
||||
</button>
|
||||
</.form>
|
||||
|
||||
<div
|
||||
:if={is_nil(@current_scope) || is_nil(@current_scope.account)}
|
||||
class="border-t border-rule bg-panel px-5 py-3 sm:px-6"
|
||||
>
|
||||
<.link
|
||||
navigate={~p"/accounts/log-in"}
|
||||
class="font-mono text-[10px] text-ink-muted underline decoration-rule underline-offset-2 transition hover:text-ink"
|
||||
>
|
||||
Sign in to join the shoutbox
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%!-- Registry state: live aggregates --%>
|
||||
<section id="registry-stats" class="border-b-2 border-strong">
|
||||
<div class="mx-auto grid w-full max-w-[90rem] grid-cols-2 sm:grid-cols-3 lg:grid-cols-5">
|
||||
<div class="border-b border-r border-rule px-4 py-6 sm:px-8 sm:py-8 lg:border-b-0">
|
||||
<p class="text-xs font-medium text-ink-faint">
|
||||
Repositories
|
||||
</p>
|
||||
<p
|
||||
id="repository-count"
|
||||
class="mt-2 font-display text-4xl font-medium tabular-nums leading-none text-ink sm:mt-3 sm:text-5xl"
|
||||
>
|
||||
{@stats.repositories}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-b border-rule px-4 py-6 sm:border-r sm:px-8 sm:py-8 lg:border-b-0">
|
||||
<p class="text-xs font-medium text-ink-faint">
|
||||
<span class="sm:hidden">No report</span>
|
||||
<span class="hidden sm:inline">No report yet</span>
|
||||
</p>
|
||||
<p
|
||||
id="unscanned-count"
|
||||
class="mt-2 font-display text-4xl font-medium tabular-nums leading-none text-signal sm:mt-3 sm:text-5xl"
|
||||
>
|
||||
{@stats.unscanned}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-b border-r border-rule px-4 py-6 sm:border-r-0 sm:px-8 sm:py-8 lg:border-b-0 lg:border-r">
|
||||
<p class="text-xs font-medium text-ink-faint">
|
||||
Contributors
|
||||
</p>
|
||||
<p
|
||||
id="contributor-count"
|
||||
class="mt-2 font-display text-4xl font-medium tabular-nums leading-none text-ink sm:mt-3 sm:text-5xl"
|
||||
>
|
||||
{@contributor_count}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-b border-rule px-4 py-6 sm:border-b sm:border-r sm:px-8 sm:py-8 lg:border-b-0">
|
||||
<p class="text-xs font-medium text-ink-faint">
|
||||
<span class="sm:hidden">Verified</span>
|
||||
<span class="hidden sm:inline">Verified findings</span>
|
||||
</p>
|
||||
<p
|
||||
id="verified-findings-count"
|
||||
class="mt-2 font-display text-4xl font-medium tabular-nums leading-none text-ink sm:mt-3 sm:text-5xl"
|
||||
>
|
||||
{@stats.verified_findings}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-span-2 border-rule px-4 py-6 sm:col-span-1 sm:px-8 sm:py-8">
|
||||
<p class="text-xs font-medium text-ink-faint">
|
||||
Online
|
||||
</p>
|
||||
<p
|
||||
id="observer-count"
|
||||
class="mt-2 font-display text-4xl font-medium tabular-nums leading-none text-phosphor sm:mt-3 sm:text-5xl"
|
||||
>
|
||||
{@observer_count}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%!-- Registry search: find a repository's public record. --%>
|
||||
<section id="repository-search" class="border-b-2 border-strong">
|
||||
<div class="mx-auto w-full max-w-[90rem]">
|
||||
<form id="repository-search-form" phx-change="search" phx-submit="search">
|
||||
<label
|
||||
for="repository-search-input"
|
||||
class="group flex cursor-text items-center gap-3 px-4 sm:px-8"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="select-none font-mono text-sm text-ink-faint transition group-focus-within:text-phosphor"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<input
|
||||
id="repository-search-input"
|
||||
type="search"
|
||||
name="q"
|
||||
value={@search_query}
|
||||
autocomplete="off"
|
||||
enterkeyhint="search"
|
||||
placeholder="search owner/name"
|
||||
phx-debounce="200"
|
||||
phx-hook="SearchShortcut"
|
||||
class="h-12 w-full min-w-0 border-0 bg-transparent font-mono text-sm text-ink outline-none placeholder:text-ink-faint focus:ring-0 sm:h-14"
|
||||
/>
|
||||
<kbd class="hidden shrink-0 border border-rule px-1.5 py-0.5 font-mono text-[10px] text-ink-faint sm:block">
|
||||
/
|
||||
</kbd>
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<div
|
||||
:if={@search_query != ""}
|
||||
id="repository-search-results"
|
||||
class="border-t border-rule"
|
||||
>
|
||||
<p
|
||||
:if={@search_results == []}
|
||||
class="px-4 py-6 font-mono text-xs text-ink-faint sm:px-8"
|
||||
>
|
||||
No repositories match “{@search_query}”.
|
||||
</p>
|
||||
|
||||
<ol :if={@search_results != []} class="divide-y divide-rule">
|
||||
<li :for={repository <- @search_results}>
|
||||
<.link
|
||||
id={"search-result-#{repository.id}"}
|
||||
navigate={TarakanWeb.RepositoryPaths.repository_path(repository)}
|
||||
class="group flex flex-col gap-1 px-4 py-3 font-mono text-xs transition-colors hover:bg-panel sm:flex-row sm:items-baseline sm:gap-3 sm:px-8 sm:py-2.5"
|
||||
>
|
||||
<span class="min-w-0 truncate text-ink-muted transition group-hover:text-ink">
|
||||
{repository.owner}/{repository.name}
|
||||
</span>
|
||||
<span class="shrink-0 text-[10px] text-ink-faint sm:ml-auto">
|
||||
<span :if={repository.primary_language}>
|
||||
{repository.primary_language} ·
|
||||
</span>
|
||||
★ {compact_stars(repository.stars_count)} · {Calendar.strftime(
|
||||
repository.inserted_at,
|
||||
"%Y-%m-%d"
|
||||
)}
|
||||
</span>
|
||||
</.link>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%!-- Open work: unreviewed repositories and explicitly requested jobs. --%>
|
||||
<section id="work-queue" class="border-b-2 border-strong">
|
||||
<div class="mx-auto w-full max-w-[90rem] px-4 py-8 sm:px-8 sm:py-14">
|
||||
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<h2 class="font-display text-2xl font-medium uppercase leading-none tracking-[0.02em] text-ink sm:text-4xl">
|
||||
Open work
|
||||
</h2>
|
||||
</div>
|
||||
<div class="flex flex-col items-start gap-2 sm:items-end">
|
||||
<p class="font-mono text-[11px] text-ink-faint">
|
||||
{length(@scan_queue)} without reports · {length(@open_tasks)} open {if length(
|
||||
@open_tasks
|
||||
) ==
|
||||
1,
|
||||
do: "job",
|
||||
else: "jobs"}
|
||||
</p>
|
||||
<.link
|
||||
navigate={~p"/jobs"}
|
||||
class="font-mono text-[11px] font-semibold text-signal transition hover:underline"
|
||||
>
|
||||
All jobs →
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<div class="grid min-w-0 border-2 border-strong lg:grid-cols-[minmax(0,1.35fr)_minmax(19rem,0.65fr)]">
|
||||
<div id="scan-queue" class="min-w-0 lg:border-r lg:border-rule">
|
||||
<div class="border-b border-rule bg-panel px-5 py-4 sm:px-6">
|
||||
<div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1">
|
||||
<h3 class="font-display text-sm uppercase tracking-[0.12em] text-ink">
|
||||
Repositories to report
|
||||
</h3>
|
||||
<span class="font-mono text-[11px] text-ink-faint">
|
||||
Recently added
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :if={@scan_queue == []} class="px-5 py-10 sm:px-6">
|
||||
<p class="font-mono text-xs text-ink-faint">
|
||||
Every registered repository has a report.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ol :if={@scan_queue != []} class="divide-y divide-rule">
|
||||
<li :for={{repository, position} <- Enum.with_index(@scan_queue, 1)}>
|
||||
<.link
|
||||
id={"scan-queue-#{repository.id}"}
|
||||
navigate={TarakanWeb.RepositoryPaths.repository_path(repository)}
|
||||
class="group grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-4 px-5 py-4 transition-colors hover:bg-panel sm:px-6"
|
||||
>
|
||||
<span class="font-mono text-[10px] tabular-nums text-ink-faint">
|
||||
{String.pad_leading(Integer.to_string(position), 2, "0")}
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-sm font-semibold text-ink">
|
||||
{repository.owner}/<span class="font-bold">{repository.name}</span>
|
||||
</p>
|
||||
<p class="mt-1 flex flex-wrap items-center gap-x-2 font-mono text-[11px] text-ink-faint">
|
||||
<span :if={repository.primary_language}>{repository.primary_language}</span>
|
||||
<span :if={repository.primary_language} aria-hidden="true">·</span>
|
||||
<span>★ {compact_stars(repository.stars_count || 0)}</span>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>Registered {Calendar.strftime(repository.inserted_at, "%b %-d, %Y")}</span>
|
||||
</p>
|
||||
</div>
|
||||
<.icon
|
||||
name="hero-arrow-right-mini"
|
||||
class="size-3.5 text-ink-faint transition group-hover:text-ink"
|
||||
/>
|
||||
</.link>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="border-t border-rule px-5 py-3 sm:px-6">
|
||||
<p class="break-all font-mono text-[10px] text-ink-faint">
|
||||
<span class="uppercase tracking-[0.12em]">API</span>
|
||||
<span class="mx-1.5">·</span> GET /api/repositories?status=unscanned
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside id="open-review-work" class="border-t border-rule lg:border-t-0">
|
||||
<div class="border-b border-rule bg-panel px-5 py-4 sm:px-6">
|
||||
<h3 class="font-display text-sm uppercase tracking-[0.12em] text-ink">
|
||||
Jobs
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div :if={@open_tasks == []} class="px-5 py-10 sm:px-6 lg:py-12">
|
||||
<p class="text-sm font-medium text-ink">No jobs are open right now.</p>
|
||||
<p class="mt-2 max-w-sm text-xs leading-5 text-ink-faint">
|
||||
Publish a Report with <code class="font-mono text-ink">tarakan worker</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul :if={@open_tasks != []} class="divide-y divide-rule">
|
||||
<li :for={task <- @open_tasks}>
|
||||
<.link
|
||||
id={"open-task-#{task.id}"}
|
||||
navigate={~p"/jobs/#{task.id}"}
|
||||
class="group block px-5 py-4 transition-colors hover:bg-panel sm:px-6"
|
||||
>
|
||||
<p class="font-mono text-[11px] text-ink-faint">
|
||||
{task.repository.owner}/{task.repository.name}
|
||||
<span class="mx-1">·</span>
|
||||
<span title={task.commit_sha}>{String.slice(task.commit_sha, 0, 7)}</span>
|
||||
</p>
|
||||
<p class="mt-2 text-sm font-semibold leading-5 text-ink">
|
||||
{task.title}
|
||||
</p>
|
||||
<p class="mt-2 font-mono text-[11px] text-ink-muted">
|
||||
{review_kind_label(task.kind)} · {provenance_label(task.capability)} required
|
||||
</p>
|
||||
</.link>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%!-- Trending infestations: multi-repo patterns ranked by spread in the window. --%>
|
||||
<section id="home-infestations" class="border-b-2 border-strong">
|
||||
<div class="mx-auto w-full max-w-[90rem] px-4 py-8 sm:px-8 sm:py-12">
|
||||
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<h2 class="font-display text-2xl font-medium uppercase leading-none tracking-[0.02em] text-ink sm:text-4xl">
|
||||
Trending infestations
|
||||
</h2>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div
|
||||
id="home-infestations-window"
|
||||
class="flex border border-strong font-display text-[10px] uppercase tracking-[0.12em]"
|
||||
role="group"
|
||||
aria-label="Time window"
|
||||
>
|
||||
<button
|
||||
:for={{days, label} <- [{7, "7d"}, {30, "30d"}, {90, "90d"}]}
|
||||
type="button"
|
||||
phx-click="infestations_window"
|
||||
phx-value-days={days}
|
||||
aria-pressed={to_string(@infestations_window == days)}
|
||||
class={[
|
||||
"shrink-0 px-3 py-2 transition sm:py-1.5",
|
||||
@infestations_window == days && "bg-btn text-btn-fg",
|
||||
@infestations_window != days && "text-ink-faint hover:text-ink"
|
||||
]}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</div>
|
||||
<.link
|
||||
navigate={~p"/infestations"}
|
||||
class="font-mono text-[11px] font-semibold uppercase tracking-[0.12em] text-signal transition hover:underline"
|
||||
>
|
||||
All infestations →
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:if={@infestations == []}
|
||||
id="home-infestations-empty"
|
||||
class="bg-panel px-5 py-12 text-center sm:px-6"
|
||||
>
|
||||
<p class="text-sm text-ink-muted">
|
||||
Infestations appear when the same issue title hits 3+ listed repos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div :if={@infestations != []} id="home-infestations-list">
|
||||
<.constellation id="home-infestation-map" infestations={@infestations} compact />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%!--
|
||||
Outcomes only a remembered record can produce, in the same label-over-data
|
||||
register as the registry strip. The explanation lives in "How it works";
|
||||
this is the evidence. Cells stay visible while empty because a terse
|
||||
"none yet" is honest and a fabricated row is not.
|
||||
--%>
|
||||
<section
|
||||
:if={
|
||||
@carried_fixes != [] or @regressions != [] or @responsive != [] or @model_precision != []
|
||||
}
|
||||
id="record-signals"
|
||||
class="border-b-2 border-strong"
|
||||
>
|
||||
<div class="mx-auto w-full max-w-[90rem] px-4 pt-8 sm:px-8 sm:pt-12">
|
||||
<h2 class="font-display text-2xl font-medium uppercase leading-none tracking-[0.02em] text-ink sm:text-4xl">
|
||||
Signals
|
||||
</h2>
|
||||
</div>
|
||||
<%!-- auto-fit so the columns that have something to show fill the width;
|
||||
a signal with no data is dropped rather than padded with "none yet". --%>
|
||||
<div class="mx-auto mt-6 grid w-full max-w-[90rem] gap-px bg-rule sm:grid-cols-[repeat(auto-fit,minmax(18rem,1fr))]">
|
||||
<div :if={@carried_fixes != []} class="bg-ground px-4 py-6 sm:px-8">
|
||||
<h3 class="text-xs font-medium text-ink-faint">Fixes carried</h3>
|
||||
<ul class="mt-2 space-y-2">
|
||||
<li :for={fix <- Enum.take(@carried_fixes, 3)} class="min-w-0">
|
||||
<.link
|
||||
navigate={~p"/infestations/#{fix.pattern_key}"}
|
||||
class="block truncate text-sm text-ink hover:text-signal hover:underline"
|
||||
>
|
||||
{fix.title}
|
||||
</.link>
|
||||
<p class="truncate font-mono text-[10px] text-ink-faint">
|
||||
{fix.source_owner}/{fix.source_name}
|
||||
<span class="text-phosphor">→</span>
|
||||
{fix.carried_to} repos
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div :if={@regressions != []} class="bg-ground px-4 py-6 sm:px-8">
|
||||
<h3 class="text-xs font-medium text-ink-faint">Regressed</h3>
|
||||
<ul class="mt-2 space-y-2">
|
||||
<li :for={regression <- Enum.take(@regressions, 3)} class="min-w-0">
|
||||
<.link
|
||||
navigate={~p"/findings/#{regression.canonical_finding.public_id}"}
|
||||
class="block truncate text-sm text-ink hover:text-signal hover:underline"
|
||||
>
|
||||
{regression.canonical_finding.title}
|
||||
</.link>
|
||||
<p class="truncate font-mono text-[10px] text-ink-faint">
|
||||
{regression.repository.owner}/{regression.repository.name}
|
||||
<span class="text-signal">↺</span>
|
||||
{String.slice(regression.detected_commit_sha, 0, 7)}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div :if={@responsive != []} class="bg-ground px-4 py-6 sm:px-8">
|
||||
<h3 class="text-xs font-medium text-ink-faint">Fastest to fix</h3>
|
||||
<ol class="mt-2 space-y-2">
|
||||
<li :for={row <- Enum.take(@responsive, 3)}>
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<.link
|
||||
navigate={TarakanWeb.RepositoryPaths.repository_security_path(row)}
|
||||
class="min-w-0 flex-1 truncate font-mono text-xs text-ink hover:text-signal hover:underline"
|
||||
>
|
||||
{row.owner}/{row.name}
|
||||
</.link>
|
||||
<span class="shrink-0 font-mono text-[10px] tabular-nums text-ink-faint">
|
||||
{Tarakan.SecurityPosture.format_days(row.median_days_to_fix)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-1 h-0.5 w-full bg-rule">
|
||||
<div
|
||||
class="h-full bg-phosphor"
|
||||
style={"width: #{home_bar_pct(row.median_days_to_fix, @responsive_max_days)}%"}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div :if={@model_precision != []} class="bg-ground px-4 py-6 sm:px-8">
|
||||
<h3 class="text-xs font-medium text-ink-faint">Model precision</h3>
|
||||
<ol class="mt-2 space-y-2">
|
||||
<li :for={row <- Enum.take(@model_precision, 3)}>
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<.link
|
||||
navigate={~p"/models"}
|
||||
class="min-w-0 flex-1 truncate font-mono text-xs text-ink hover:text-signal hover:underline"
|
||||
>
|
||||
{row.model}
|
||||
</.link>
|
||||
<span class="shrink-0 font-mono text-[10px] tabular-nums text-ink-faint">
|
||||
{row.precision}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-1 h-0.5 w-full bg-rule">
|
||||
<div class="h-full bg-phosphor" style={"width: #{row.precision}%"}></div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="how-it-works" class="border-b-2 border-strong bg-panel">
|
||||
<div class="mx-auto grid w-full max-w-[90rem] gap-8 px-4 py-10 sm:gap-10 sm:px-8 sm:py-20 lg:grid-cols-[minmax(16rem,0.72fr)_minmax(0,1.28fr)] lg:gap-16">
|
||||
<div>
|
||||
<h2 class="max-w-md font-display text-2xl font-medium uppercase leading-none tracking-[0.02em] text-ink sm:text-4xl">
|
||||
How it works
|
||||
</h2>
|
||||
<p class="mt-5 max-w-md text-sm leading-6 text-ink-muted">
|
||||
Run the agent on your machine. Findings stay on a public record.
|
||||
</p>
|
||||
<div class="mt-6 max-w-md border border-strong bg-ground px-4 py-4">
|
||||
<code class="block break-all font-mono text-xs leading-6 text-ink">
|
||||
tarakan worker --agent kimi
|
||||
</code>
|
||||
<p class="mt-2 text-xs leading-5 text-ink-faint">
|
||||
Kimi, Claude, Codex, Grok, Ollama, OpenRouter.
|
||||
<.link navigate={~p"/agents"} class="text-signal hover:underline">Install</.link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ol class="border-t-2 border-strong">
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-ink">
|
||||
1. Report
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Pin a commit and publish findings. Public on submit. No Job required.
|
||||
</p>
|
||||
</li>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-ink">
|
||||
2. Check
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Someone else re-runs the same commit. Confirm, dispute, or mark fixed.
|
||||
</p>
|
||||
</li>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-ink">3. Job</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Optional pickup ticket. Reports with findings open a check Job automatically.
|
||||
</p>
|
||||
</li>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-ink">
|
||||
4. Merge
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Duplicate findings merge into one issue. Stronger evidence ranks higher.
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<%!-- The loop above is what a contributor does. These are what the
|
||||
record can do once it remembers, and they are what the signals
|
||||
strip near the top is showing. --%>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-signal">
|
||||
Then: carry
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
The same bug lives in codebases that never shared a line. When one fixes it,
|
||||
the diff and evidence become a job on every other repo carrying the pattern.
|
||||
</p>
|
||||
</li>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-signal">
|
||||
Then: regress
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Findings are pinned to exact commits, so a bug that comes back is recorded
|
||||
against the original instead of filed again as new.
|
||||
</p>
|
||||
</li>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-signal">
|
||||
Then: rank
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Median days from verified to fixed, per repository. Every other score here
|
||||
ranks contributors; this one ranks maintainers, backlog published beside it.
|
||||
</p>
|
||||
</li>
|
||||
<li class="grid gap-2 border-b border-rule py-6 sm:grid-cols-[minmax(8rem,0.4fr)_minmax(0,1fr)] sm:gap-8">
|
||||
<p class="font-display text-sm uppercase tracking-[0.12em] text-signal">
|
||||
Then: score
|
||||
</p>
|
||||
<p class="max-w-xl text-sm leading-6 text-ink-muted">
|
||||
Contributors run their own agents, so the same commit meets different models.
|
||||
Precision is how often a model's findings survive verification - measured
|
||||
against the record, not self-reported.
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</Layouts.app>
|
||||
Loading…
Add table
Add a link
Reference in a new issue