zoeys.computer/lib/zoeyscomputer_web/components/code_block.ex
2024-10-26 15:01:33 -04:00

93 lines
2.9 KiB
Elixir

defmodule ZoeyscomputerWeb.CodeBlock do
use Phoenix.Component
alias Phoenix.LiveView.JS
@moduledoc """
A code block component with syntax highlighting using Shiki.
## Features:
- Syntax highlighting with Shiki
- Optional line numbers
- Optional highlighted lines
- Optional title
- Copy button
- Catppuccin theme styling
"""
@doc """
Renders a code block with syntax highlighting.
## Examples
<.code_block
code="def hello, do: :world"
language="elixir"
title="Example Code"
line_numbers={true}
highlighted_lines={[1, 3]}
/>
## Options
* `:code` - Required. The code string to highlight
* `:language` - Required. Programming language for syntax highlighting
* `:title` - Optional. Title displayed above the code block
* `:line_numbers` - Optional. Show line numbers (default: false)
* `:highlighted_lines` - Optional. List of line numbers to highlight
"""
attr :code, :string, required: true
attr :language, :string, required: true
attr :title, :string, default: nil
attr :line_numbers, :boolean, default: false
attr :highlighted_lines, :list, default: []
def code_block(assigns) do
# Calculate the number of lines for line numbers
assigns = assign(assigns, :num_lines, String.split(assigns.code, "\n") |> length())
~H"""
<div class="relative ctp-bg-base rounded-lg overflow-hidden">
<%= if @title do %>
<div class="ctp-bg-mantle px-4 py-2 border-b ctp-border-surface0">
<h3 class="ctp-text-text text-sm font-medium"><%= @title %></h3>
</div>
<% end %>
<div class="relative">
<%= if @line_numbers do %>
<div class="absolute left-0 top-0 bottom-0 ctp-bg-crust w-12 flex flex-col items-end pr-2 py-4 ctp-text-surface2 select-none">
<%= for line_num <- 1..@num_lines do %>
<span class={[
"text-sm leading-6",
line_num in @highlighted_lines && "ctp-text-mauve font-medium"
]}>
<%= line_num %>
</span>
<% end %>
</div>
<% end %>
<div class={["overflow-x-auto", @line_numbers && "pl-12"]}>
<pre
class="p-4"
id={"code-block-#{System.unique_integer()}"}
phx-hook="CodeBlockHook"
data-code={@code}
data-language={@language}
data-highlighted-lines={Jason.encode!(@highlighted_lines)}
><code class="text-sm"><%= @code %></code></pre>
</div>
</div>
<div class="ctp-bg-mantle px-4 py-2 border-t ctp-border-surface0 flex justify-end">
<button
type="button"
class="ctp-text-subtext0 hover:ctp-text-text text-sm"
phx-click={JS.dispatch("clipcopy", detail: %{text: @code})}
>
Copy code
</button>
</div>
</div>
"""
end
end