Dark Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d47fdbd

Browse files
authored
1 parent c29b973 commit d47fdbd

File tree

1 file changed

+212
-0
lines changed
  • github-issue-to-markdown.html

1 file changed

+212
-0
lines changed

github-issue-to-markdown.html

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
>
2+
<html>
3+
<head>
4+
<style>
5+
* {
6+
box-sizing: border-box;
7+
}
8+
9+
body {
10+
font-family: Helvetica, Arial, sans-serif;
11+
max-width: 800px;
12+
margin: 0 auto;
13+
padding: 20px;
14+
}
15+
16+
h1 {
17+
margin-bottom: 24px;
18+
}
19+
20+
.input-group {
21+
margin-bottom: 24px;
22+
}
23+
24+
label {
25+
display: block;
26+
margin-bottom: 8px;
27+
}
28+
29+
input {
30+
width: 100%;
31+
padding: 8px;
32+
font-size: 16px;
33+
border: 1px solid #ccc;
34+
border-radius: 4px;
35+
}
36+
37+
button {
38+
background: #2ea44f;
39+
color: white;
40+
border: none;
41+
padding: 8px 16px;
42+
border-radius: 4px;
43+
cursor: pointer;
44+
}
45+
46+
button:hover {
47+
background: #2c974b;
48+
}
49+
50+
button:disabled {
51+
background: #94d3a2;
52+
cursor: not-allowed;
53+
}
54+
55+
.output {
56+
margin-top: 24px;
57+
}
58+
59+
textarea {
60+
width: 100%;
61+
min-height: 300px;
62+
padding: 12px;
63+
font-size: 16px;
64+
font-family: monospace;
65+
border: 1px solid #ccc;
66+
border-radius: 4px;
67+
resize: vertical;
68+
}
69+
70+
.error {
71+
color: #cf222e;
72+
margin-top: 8px;
73+
}
74+
75+
.copy-button {
76+
margin-top: 12px;
77+
}
78+
style>
79+
head>
80+
<body>
81+
<h1>Convert GitHub issue to markdownh1>
82+
83+
<div class="input-group">
84+
<label for="issue-url">GitHub issue URLlabel>
85+
<input
86+
type="text"
87+
id="issue-url"
88+
placeholder="https://github.com/owner/repo/issues/123"
89+
>
90+
div>
91+
92+
<button id="convert">Convert to markdownbutton>
93+
94+
<div class="output">
95+
<textarea id="markdown-output" readonly>textarea>
96+
<button class="copy-button" id="copy">Copy to clipboardbutton>
97+
div>
98+
99+
<p class="error" id="error">p>
100+
101+
<script type="module">
102+
const urlInput = document.getElementById('issue-url')
103+
const convertButton = document.getElementById('convert')
104+
const markdownOutput = document.getElementById('markdown-output')
105+
const copyButton = document.getElementById('copy')
106+
const errorElement = document.getElementById('error')
107+
108+
function parseGitHubUrl(url) {
109+
try {
110+
const urlObj = new URL(url)
111+
const [, owner, repo, , number] = urlObj.pathname.split('/')
112+
return { owner, repo, number }
113+
} catch (e) {
114+
throw new Error('Invalid GitHub URL')
115+
}
116+
}
117+
118+
function convertToMarkdown(issue, comments) {
119+
let md = `# ${issue.title}\n\n`
120+
md += `*Posted by @${issue.user.login}*\n\n`
121+
md += issue.body + '\n\n'
122+
123+
if (comments.length > 0) {
124+
md += '---\n\n'
125+
comments.forEach(comment => {
126+
md += `### Comment by @${comment.user.login}\n\n`
127+
md += comment.body + '\n\n'
128+
md += '---\n\n'
129+
})
130+
}
131+
132+
return md
133+
}
134+
135+
async function getAllPages(url) {
136+
let allItems = []
137+
let nextUrl = url
138+
139+
while (nextUrl) {
140+
const response = await fetch(nextUrl)
141+
142+
if (!response.ok) {
143+
throw new Error('Failed to fetch data')
144+
}
145+
146+
const items = await response.json()
147+
allItems = allItems.concat(items)
148+
149+
// Check for pagination in Link header
150+
const link = response.headers.get('Link')
151+
nextUrl = null
152+
153+
if (link) {
154+
const nextLink = link.split(',').find(s => s.includes('rel="next"'))
155+
if (nextLink) {
156+
nextUrl = nextLink.split(';')[0].trim().slice(1, -1)
157+
}
158+
}
159+
}
160+
161+
return allItems
162+
}
163+
164+
async function fetchIssueAndComments(owner, repo, number) {
165+
const issueUrl = `https://api.github.com/repos/${owner}/${repo}/issues/${number}`
166+
const commentsUrl = `${issueUrl}/comments`
167+
168+
const [issue, comments] = await Promise.all([
169+
fetch(issueUrl).then(res => {
170+
if (!res.ok) throw new Error('Failed to fetch issue')
171+
return res.json()
172+
}),
173+
getAllPages(commentsUrl)
174+
])
175+
176+
return { issue, comments }
177+
}
178+
179+
convertButton.addEventListener('click', async () => {
180+
errorElement.textContent = ''
181+
markdownOutput.value = ''
182+
convertButton.disabled = true
183+
184+
try {
185+
const { owner, repo, number } = parseGitHubUrl(urlInput.value)
186+
const { issue, comments } = await fetchIssueAndComments(owner, repo, number)
187+
const markdown = convertToMarkdown(issue, comments)
188+
markdownOutput.value = markdown
189+
} catch (error) {
190+
errorElement.textContent = error.message
191+
} finally {
192+
convertButton.disabled = false
193+
}
194+
})
195+
196+
copyButton.addEventListener('click', () => {
197+
markdownOutput.select()
198+
document.execCommand('copy')
199+
200+
const originalText = copyButton.textContent
201+
copyButton.textContent = 'Copied'
202+
copyButton.disabled = true
203+
204+
setTimeout(() => {
205+
copyButton.textContent = originalText
206+
copyButton.disabled = false
207+
}, 2000)
208+
})
209+
210+
script>
211+
body>
212+
html>

0 commit comments

Comments
(0)