defmodule TarakanWeb.RepositoryComponents do @moduledoc """ Shared repository page chrome. Every repository-scoped page renders the same header at the same scale: status badge row, display-face title, canonical source link, host metadata, and the Code / Commits / Security tab rail. Page bodies differ; the frame does not. """ use Phoenix.Component import TarakanWeb.CoreComponents, only: [icon: 1, breadcrumbs: 1, handle_link: 1, notch_badge: 1] alias TarakanWeb.RepositoryPaths @doc ~S'Formats a star count compactly (`1200` -> `"1.2k"`); `nil` -> `"0"`.' def compact_stars(count) when is_integer(count) and count >= 1000, do: "#{Float.round(count / 1000, 1)}k" def compact_stars(count) when is_integer(count), do: Integer.to_string(count) def compact_stars(_count), do: "0" attr :repository, Tarakan.Repositories.Repository, required: true attr :active_tab, :atom, required: true, values: [:code, :commits, :security] def repository_header(assigns) do ~H"""
<.breadcrumbs id="repository-breadcrumb"> <:crumb navigate="/">registry <:crumb navigate={RepositoryPaths.repository_path(@repository)}> {@repository.owner} <:crumb navigate={RepositoryPaths.repository_path(@repository)}> {@repository.name}
<.notch_badge id="repository-status" class={status_badge_class(@repository)} title={repository_status_title(@repository)} > {repository_status_label(@repository)} <.notch_badge :if={@repository.archived} class="text-signal"> archived <.notch_badge :if={@repository.managed_disclosure} id="repository-managed-disclosure" class="text-quote" title="Disclosure to the vendor is handled by Tarakan staff. Handling only - the public record is unchanged." > Managed disclosure

{@repository.owner}/{@repository.name}

{@repository.description}

{@repository.canonical_url} <.icon name="hero-arrow-up-right" class="size-4 shrink-0" />
{@repository.primary_language} <.icon name="hero-star" class="size-3.5" /> {@repository.stars_count} stars <.icon name="hero-code-bracket" class="size-3.5" /> {@repository.forks_count} forks hosted on tarakan default: {@repository.default_branch} <.icon name="hero-clock" class="size-3.5" /> registered {Calendar.strftime( @repository.inserted_at, "%Y-%m-%d" )} by <.handle_link handle={@repository.submitted_by.handle} class="text-ink-muted" />
""" end attr :id, :string, required: true attr :active, :boolean, required: true attr :navigate, :string, required: true attr :icon, :string, required: true attr :label, :string, required: true slot :inner_block # The active tab stays a link. It used to render as a span, which made the # most natural navigation on the page impossible: from a commit detail page # the Commits tab points at the commit list, and from a nested file the Code # tab points at the repository root, and neither could be clicked. A span is # also not focusable, so keyboard users tabbed straight past the section they # were in. aria-current carries the "you are here" meaning instead. defp repository_tab(assigns) do ~H""" <.link id={@id} navigate={@navigate} aria-current={@active && "page"} class={[ "-mb-px inline-flex shrink-0 items-center gap-2 border-b-2 px-3 py-3 transition sm:px-4", @active && "border-signal font-semibold text-ink", !@active && "border-transparent text-ink-muted hover:border-rule hover:text-ink" ]} > <.icon name={@icon} class="size-4" /> {@label} {render_slot(@inner_block)} """ end @doc """ The scan's submitted Tarakan Scan Format document, pretty-printed for the raw-report block. Returns the stored text unchanged when it isn't JSON. """ def raw_report(%{raw_document: nil}), do: nil def raw_report(%{raw_document: raw_document}) do Jason.Formatter.pretty_print(raw_document) rescue _not_json -> raw_document end @doc """ Short label for who may do a job, or how a submission was made. Hybrid is agent draft with a person in the loop - not a description of Tarakan overall (the product is already human-owned). """ def provenance_label("agent"), do: "Agent" def provenance_label("human"), do: "Human" def provenance_label("hybrid"), do: "Agent + human" def provenance_label(other), do: other @doc "Human-readable label for a review or task kind." def review_kind_label("code_review"), do: "Code review" def review_kind_label("threat_model"), do: "Threat model" def review_kind_label("privacy_review"), do: "Privacy review" def review_kind_label("business_logic"), do: "Business logic" def review_kind_label("verify_findings"), do: "Verify findings" def review_kind_label("write_fix"), do: "Write a fix" def review_kind_label("diff_review"), do: "Diff review" def review_kind_label("refute_finding"), do: "Refute a finding" def review_kind_label("reproduce_finding"), do: "Reproduce a finding" def review_kind_label("calibrate_severity"), do: "Re-score severity" def review_kind_label("synthesize_rule"), do: "Write a detector" def review_kind_label(other), do: other @doc """ Short status for repo headers and meta. Avoids the useless lone label \"findings\" once every repo has some. Prefers open/verified counts and clear/unreviewed states. """ def repository_status_label(%{status: "unscanned"}), do: "No report" def repository_status_label(%{open_findings_count: open, verified_findings_count: verified}) when is_integer(open) and open > 0 do open_label = if open == 1, do: "1 open", else: "#{open} open" if is_integer(verified) and verified > 0 do verified_label = if verified == 1, do: "1 verified", else: "#{verified} verified" "#{open_label} ยท #{verified_label}" else open_label end end def repository_status_label(%{status: status}) when status in ["reviewed", "clear"], do: "Clear" def repository_status_label(%{scan_count: n}) when is_integer(n) and n > 0, do: "Clear" def repository_status_label(_repository), do: "No report" defp repository_status_title(%{open_findings_count: open, verified_findings_count: verified}) when is_integer(open) and open > 0 do open_part = if open == 1, do: "1 unique finding still open", else: "#{open} unique findings still open" if is_integer(verified) and verified > 0 do verified_part = if verified == 1, do: "1 independently verified", else: "#{verified} independently verified" "#{open_part}; #{verified_part}" else open_part end end defp repository_status_title(%{status: "unscanned"}), do: "No public review on the record yet" defp repository_status_title(%{status: status}) when status in ["reviewed", "clear"], do: "Reported with no open findings" defp repository_status_title(%{scan_count: n}) when is_integer(n) and n > 0, do: "Reported with no open findings" defp repository_status_title(_repository), do: "No public review on the record yet" defp status_badge_class(%{open_findings_count: open}) when is_integer(open) and open > 0, do: "text-signal" defp status_badge_class(%{status: "unscanned"}), do: "text-ink-faint" defp status_badge_class(%{status: status}) when status in ["reviewed", "clear"], do: "text-quote" defp status_badge_class(_repository), do: "text-ink-muted" # Tab pill: open count when > 0, else verified count, else nothing for unscanned. defp security_tab_count(%{open_findings_count: open}) when is_integer(open) and open > 0, do: open defp security_tab_count(%{verified_findings_count: verified}) when is_integer(verified) and verified > 0, do: verified defp security_tab_count(%{scan_count: scans}) when is_integer(scans) and scans > 0, do: "ok" defp security_tab_count(_repository), do: nil defp security_tab_count_class(%{open_findings_count: open}) when is_integer(open) and open > 0, do: "bg-signal text-ground" defp security_tab_count_class(_repository), do: "bg-panel text-ink-muted border border-rule" end