Skip to content

Commit 69fa96f

Browse files
committed
Add view github repo
1 parent e04c1c2 commit 69fa96f

File tree

4 files changed

+487
-0
lines changed

4 files changed

+487
-0
lines changed

lib/components_guide/git/pkt_line.ex

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
defmodule ComponentsGuide.Git.PktLine do
2+
defstruct ref: "", oid: "", attrs: %{}, attrs_raw: []
3+
4+
def decode_line_string(line) do
5+
parts = line |> String.trim_trailing() |> String.split(" ")
6+
7+
case parts do
8+
["#" | _] ->
9+
nil
10+
11+
[oid, ref_raw | attrs_raw] ->
12+
[ref | _] = ref_raw |> String.split("\u0000")
13+
attrs = for attr_raw <- attrs_raw do
14+
# case String.split(attr_raw, ":", parts: 2) do
15+
case String.split(attr_raw, "=", parts: 2) do
16+
[attr] ->
17+
{attr, true}
18+
[key, value] ->
19+
{key, value}
20+
21+
end
22+
end
23+
%__MODULE__{oid: oid, ref: ref, attrs: attrs, attrs_raw: attrs_raw}
24+
25+
_ ->
26+
nil
27+
end
28+
end
29+
30+
def decode_line(<<length_hex::bytes-size(4)>> <> data = full_data) when is_binary(data) do
31+
case Integer.parse(length_hex, 16) do
32+
:error ->
33+
IO.puts("Error parsing #{full_data}")
34+
35+
{0, _} ->
36+
{nil, data}
37+
38+
{1, _} ->
39+
{nil, data}
40+
41+
{length, _} ->
42+
length = length - 4
43+
<<line_bytes::bytes-size(length)>> <> data = data
44+
45+
case line_bytes do
46+
<<>> ->
47+
{nil, <<>>}
48+
49+
line_bytes ->
50+
decoded = decode_line_string(line_bytes)
51+
{decoded, data}
52+
end
53+
end
54+
end
55+
56+
# Invalid short line
57+
def decode_line(data) when is_binary(data), do: {nil, data}
58+
59+
def decode_lines(<<>>, lines) do
60+
Enum.reverse(lines)
61+
end
62+
63+
def decode_lines(data, lines) when is_binary(data) do
64+
case decode_line(data) do
65+
{nil, data} ->
66+
decode_lines(data, lines)
67+
68+
{line, data} ->
69+
decode_lines(data, [line | lines])
70+
end
71+
end
72+
73+
def decode(data) when is_binary(data) do
74+
decode_lines(data, [])
75+
# return function* decodePktLine() {
76+
# let current = 0
77+
# linesLoop: while (true) {
78+
# const utf8Decoder = new TextDecoder('utf-8')
79+
# const lengthHex = utf8Decoder.decode(
80+
# arrayBuffer.slice(current, current + 4),
81+
# )
82+
# current += 4
83+
# const length = parseInt(lengthHex, '16')
84+
# if (length <= 1) {
85+
# continue linesLoop
86+
# }
87+
88+
# const bytes = arrayBuffer.slice(current, current + length - 4)
89+
# if (bytes.byteLength === 0) break linesLoop
90+
# current += length - 4
91+
92+
# const line = utf8Decoder.decode(bytes).trimEnd()
93+
# const [oid, refRaw, ...attrs] = line.split(' ')
94+
# if (oid === '#') {
95+
# continue linesLoop
96+
# }
97+
98+
# const [ref] = refRaw.split('\u0000')
99+
100+
# const r = { ref, oid }
101+
# // r.attrs = attrs;
102+
# for (const attr of attrs) {
103+
# const [name, value] = attr.split(':')
104+
# if (name === 'symref-target') {
105+
# r.target = value
106+
# } else if (name === 'peeled') {
107+
# r.peeled = value
108+
# } else if (name === 'symref=HEAD') {
109+
# r.HEADRef = value
110+
# } else if (name === 'object-format') {
111+
# r.objectFormat = value
112+
# } else if (name === 'agent') {
113+
# r.agent = value
114+
# }
115+
# }
116+
# yield Object.freeze(r)
117+
# }
118+
# }
119+
end
120+
end
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
defmodule ComponentsGuideWeb.ViewGithubRepoLive do
2+
use ComponentsGuideWeb,
3+
{:live_view, container: {:div, class: "max-w-6xl mx-auto text-lg text-white pb-24"}}
4+
5+
alias ComponentsGuide.Fetch
6+
7+
defmodule State do
8+
defstruct owner: "",
9+
repo: "",
10+
request: nil,
11+
response: nil
12+
13+
def default() do
14+
%__MODULE__{
15+
owner: "JavaScriptRegenerated",
16+
repo: "yieldmachine"
17+
# owner: "facebook",
18+
# repo: "react"
19+
}
20+
end
21+
22+
def add_response(
23+
%__MODULE__{} = state,
24+
request = %Fetch.Request{},
25+
response = %Fetch.Response{}
26+
) do
27+
%__MODULE__{
28+
state
29+
| request: request,
30+
response: response
31+
}
32+
end
33+
end
34+
35+
@impl true
36+
def render(assigns) do
37+
~H"""
38+
<.form
39+
let={f}
40+
for={:editor}
41+
id="view_source_form"
42+
phx-submit="submitted"
43+
class="max-w-2xl mt-12 mx-auto space-y-2"
44+
>
45+
46+
<fieldset y-y y-stretch class="gap-1">
47+
<label for="owner">Owner:</label>
48+
<input id="owner" type="text" name="owner" value={@state.owner} class="text-black">
49+
</fieldset>
50+
51+
<fieldset y-y y-stretch class="gap-1">
52+
<label for="repo">Repo name:</label>
53+
<input id="repo" type="text" name="repo" value={@state.repo} class="text-black">
54+
</fieldset>
55+
56+
<div class="flex">
57+
<fieldset class="flex items-center gap-2">
58+
<label for="head-radio">
59+
<input id="head-radio" type="radio" name="method" value="HEAD" checked={true} />
60+
List Branches
61+
</label>
62+
63+
<label for="get-radio">
64+
<input id="get-radio" type="radio" name="method" value="GET" checked={false} />
65+
Other
66+
</label>
67+
</fieldset>
68+
69+
<span class="mx-auto"></span>
70+
<button type="submit" class="px-3 py-1 text-blue-100 bg-blue-600 rounded">Load</button>
71+
</div>
72+
</.form>
73+
74+
<output form="view_source_form" class="prose prose-invert block pt-4 max-w-none text-center">
75+
<%= if @state.response do %>
76+
<pre><%= @state.request.method %> <%= @state.response.url %></pre>
77+
<p>
78+
Received <span class="px-2 py-1 bg-green-400 text-green-900 rounded"><%= @state.response.status %></span>
79+
in <%= System.convert_time_unit(@state.response.timings.duration, :native, :millisecond) %>ms
80+
</p>
81+
<view-source-filter>
82+
<form role="search" id="filter-results">
83+
<input name="q" type="search" placeholder="Filter results…" class="text-white bg-gray-800 border-gray-700 rounded">
84+
</form>
85+
</view-source-filter>
86+
<.headers_preview headers={@state.response.headers}>
87+
</.headers_preview>
88+
<%= if (@state.response.body || "") != "" do %>
89+
<.git_pkt_line_preview data={@state.response.body}>
90+
</.git_pkt_line_preview>
91+
<% end %>
92+
<% end %>
93+
</output>
94+
<style>
95+
dt[hidden] + dd {
96+
display: none;
97+
}
98+
</style>
99+
"""
100+
end
101+
102+
defp assign_state(socket, state) do
103+
assign(socket, state: state)
104+
end
105+
106+
@impl true
107+
def mount(%{}, _session, socket) do
108+
state = State.default()
109+
socket = assign_state(socket, state)
110+
{:ok, socket}
111+
end
112+
113+
@impl true
114+
def handle_event("changed", _form_values, socket) do
115+
{:noreply, socket}
116+
end
117+
118+
@impl true
119+
def handle_event("submitted", form_values, socket) do
120+
# state = State.from(form_values)
121+
IO.inspect(form_values)
122+
owner = Map.get(form_values, "owner")
123+
repo = Map.get(form_values, "repo")
124+
uri = URI.new!("https://github.com/owner/repo.git/info/refs?service=git-upload-pack")
125+
uri = put_in(uri.path, "/#{owner}/#{repo}.git/info/refs?service=git-upload-pack")
126+
# uri = put_in(uri.path, "/#{owner}/#{repo}.git/info/refs")
127+
IO.puts(uri |> URI.to_string())
128+
129+
request =
130+
Fetch.Request.new(uri,
131+
headers: [
132+
{"Accept", "*/*"},
133+
{"User-Agent", "git/2.20.1"}
134+
]
135+
)
136+
137+
response = Fetch.load!(request)
138+
IO.inspect(response.headers)
139+
140+
state = socket.assigns.state |> State.add_response(request, response)
141+
142+
socket = socket |> assign_state(state)
143+
{:noreply, socket}
144+
end
145+
146+
def headers_preview(assigns) do
147+
~H"""
148+
<h2>Response Headers</h2>
149+
<dl class="grid grid-cols-2 gap-y-1 font-mono break-words">
150+
<%= for {name, value} <- @headers do %>
151+
<dt class="text-right font-bold"><%= name %></dt>
152+
<dd class="text-left pl-8"><%= value %></dd>
153+
<% end %>
154+
</dl>
155+
"""
156+
end
157+
158+
def hex_preview(assigns) do
159+
~H"""
160+
<pre class="mx-auto p-0 max-w-[24ch] break-all whitespace-pre-wrap"><%= Base.encode16(@data) %></pre>
161+
"""
162+
end
163+
164+
def git_pkt_line_preview(assigns) do
165+
166+
~H"""
167+
<ul>
168+
<%= for pkt_line <- ComponentsGuide.Git.PktLine.decode(@data) do %>
169+
<li><%= inspect(pkt_line) %></li>
170+
<% end %>
171+
</ul>
172+
"""
173+
end
174+
175+
def list_html_features(html) do
176+
with {:ok, document} <- Floki.parse_document(html) do
177+
meta_values =
178+
for {"meta", attrs, _} <- Floki.find(document, "head meta"),
179+
key_value <- extract_meta_key_values(Map.new(attrs)) do
180+
key_value
181+
end
182+
183+
link_values =
184+
for {"link", attrs, _} <- Floki.find(document, "head link"),
185+
key_value <- extract_link_key_values(Map.new(attrs)) do
186+
key_value
187+
end
188+
189+
[meta_values: meta_values, link_values: link_values]
190+
else
191+
_ -> []
192+
end
193+
end
194+
195+
def extract_link_key_values(%{"rel" => rel, "href" => href}) do
196+
[{rel, href}]
197+
end
198+
199+
def extract_link_key_values(_) do
200+
[]
201+
end
202+
203+
def extract_meta_key_values(%{"name" => name, "content" => content}) do
204+
[{name, content}]
205+
end
206+
207+
def extract_meta_key_values(%{"property" => property, "content" => content}) do
208+
[{property, content}]
209+
end
210+
211+
def extract_meta_key_values(_), do: []
212+
end

lib/components_guide_web/router.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ defmodule ComponentsGuideWeb.Router do
7070
get("/calendar", CalendarController, :index)
7171

7272
live("/view-source", ViewSourceLive)
73+
live("/view-github-repo", ViewGithubRepoLive)
7374
live("/latency-calculator", LatencyCalculatorLive)
7475

7576
live("/color", ColorLive, :index)

0 commit comments

Comments
 (0)