1717 font-size : 24px ;
1818 margin-bottom : 20px ;
1919 }
20- # url-input , # format-select , # submit-btn , # markdown-raw , # copy-btn {
20+ # url-input , # format-select , # submit-btn , # markdown-raw , # copy-btn , # prompt-textarea , # run-prompt-btn {
2121 font-size : 16px ;
2222 padding : 5px ;
2323 margin-bottom : 10px ;
2929 # format-select {
3030 width : 20% ;
3131 }
32- # submit-btn , # copy-btn {
32+ # submit-btn , . copy-btn , # run-prompt -btn {
3333 background-color : # 4CAF50 ;
3434 color : white;
3535 border : 2px solid # 4CAF50 ;
3636 cursor : pointer;
3737 }
38- # submit-btn : hover , # copy-btn : hover {
38+ # submit-btn : hover , . copy-btn : hover , # run-prompt -btn: hover {
3939 background-color : # 45a049 ;
4040 }
41- # markdown-raw {
41+ # markdown-raw , # prompt-textarea {
4242 width : 100% ;
4343 height : 200px ;
4444 margin-top : 20px ;
4545 resize : vertical;
4646 }
47+ # prompt-textarea {
48+ height : 100px ;
49+ }
4750 # markdown-rendered {
4851 margin-top : 20px ;
4952 border : 1px solid # ccc ;
5053 padding : 10px ;
5154 overflow-wrap : break-word;
5255 }
53- # loading {
56+ # loading , # prompt-loading {
5457 display : none;
5558 margin-top : 20px ;
5659 }
57- # result {
60+ # result , # prompt-result {
5861 display : none;
62+ line-height : 1.5 ;
63+ font-size : 16px ;
64+ }
65+ # prompt-rendered {
66+ font-family : system-ui, -apple-system, sans-serif;
67+ max-width : 65ch ;
68+ margin : 1.5em 0 ;
69+ }
70+ # prompt-rendered p {
71+ margin : 1em 0 ;
72+ line-height : 1.6 ;
73+ }
74+ # prompt-rendered ul {
75+ margin : 1em 0 ;
76+ padding-left : 2em ;
77+ }
78+ # prompt-rendered li {
79+ margin : 0.5em 0 ;
80+ line-height : 1.4 ;
5981 }
6082 iframe {
6183 width : 100% ;
@@ -103,6 +125,17 @@ <h1>Jina Reader</h1>
103125 < iframe id ="markdown-rendered " sandbox > </ iframe >
104126 </ div >
105127
128+ < div id ="prompt-section " style ="margin-top: 2em; padding-top: 2em; border-top: 1px solid #eee; display: none; ">
129+ < h2 > Run a prompt with Claude</ h2 >
130+ < textarea id ="prompt-textarea "> Respond in markdown. You summarize the pasted in text. Start with a overall summary in a single paragraph. Then show a bullet pointed list of the most interesting illustrative quotes from the piece. Then a bullet point list of the most unusual ideas. Finally provide a longer summary that covers points not included already</ textarea >
131+ < button id ="run-prompt-btn "> Run prompt</ button >
132+ < div id ="prompt-loading " style ="margin: 1em 0; font-style: italic; "> Generating...</ div >
133+ < div id ="prompt-result ">
134+ < div id ="prompt-rendered "> </ div >
135+ < button id ="prompt-copy-btn " class ="copy-btn " style ="margin-top: 1em; "> Copy to clipboard</ button >
136+ </ div >
137+ </ div >
138+
106139 < script >
107140 const urlForm = document . getElementById ( 'url-form' ) ;
108141 const urlInput = document . getElementById ( 'url-input' ) ;
@@ -113,6 +146,14 @@ <h1>Jina Reader</h1>
113146 const copyBtn = document . getElementById ( 'copy-btn' ) ;
114147 const markdownRendered = document . getElementById ( 'markdown-rendered' ) ;
115148
149+ // Claude elements
150+ const promptTextarea = document . getElementById ( 'prompt-textarea' ) ;
151+ const runPromptBtn = document . getElementById ( 'run-prompt-btn' ) ;
152+ const promptLoading = document . getElementById ( 'prompt-loading' ) ;
153+ const promptResult = document . getElementById ( 'prompt-result' ) ;
154+ const promptRendered = document . getElementById ( 'prompt-rendered' ) ;
155+ const promptCopyBtn = document . getElementById ( 'prompt-copy-btn' ) ;
156+
116157 urlForm . addEventListener ( 'submit' , async ( e ) => {
117158 e . preventDefault ( ) ;
118159 const url = urlInput . value ;
@@ -157,11 +198,13 @@ <h1>Jina Reader</h1>
157198 }
158199 markdownRendered . srcdoc = htmlContent ;
159200 result . style . display = 'block' ;
201+ document . getElementById ( 'prompt-section' ) . style . display = 'block' ;
160202 } catch ( error ) {
161203 console . error ( 'Error fetching content:' , error ) ;
162204 markdownRaw . value = 'Error fetching content. Please try again.' ;
163205 markdownRendered . srcdoc = '<p>Error fetching content. Please try again.</p>' ;
164206 result . style . display = 'block' ;
207+ document . getElementById ( 'prompt-section' ) . style . display = 'none' ;
165208 } finally {
166209 loading . style . display = 'none' ;
167210 }
@@ -178,6 +221,87 @@ <h1>Jina Reader</h1>
178221 copyBtn . textContent = originalText ;
179222 } , 1500 ) ;
180223 } ) ;
224+
225+ // Get the API key from localStorage or prompt the user to enter it
226+ function getApiKey ( ) {
227+ let apiKey = localStorage . getItem ( "ANTHROPIC_API_KEY" ) ;
228+ if ( ! apiKey ) {
229+ apiKey = prompt ( "Please enter your Anthropic API key:" ) ;
230+ if ( apiKey ) {
231+ localStorage . setItem ( "ANTHROPIC_API_KEY" , apiKey ) ;
232+ }
233+ }
234+ return apiKey ;
235+ }
236+
237+ // Handle Claude prompt
238+ runPromptBtn . addEventListener ( 'click' , async ( ) => {
239+ const apiKey = getApiKey ( ) ;
240+ if ( ! apiKey ) {
241+ alert ( "API key not found. Please enter your Anthropic API key." ) ;
242+ return ;
243+ }
244+
245+ // Get the prompt and content
246+ const systemPrompt = promptTextarea . value ;
247+ const content = markdownRaw . value ;
248+
249+ if ( ! content ) {
250+ alert ( "No content to analyze. Please fetch a URL first." ) ;
251+ return ;
252+ }
253+
254+ promptLoading . style . display = 'block' ;
255+ promptResult . style . display = 'none' ;
256+
257+ try {
258+ const response = await fetch ( "https://api.anthropic.com/v1/messages" , {
259+ method : "POST" ,
260+ headers : {
261+ "x-api-key" : apiKey ,
262+ "anthropic-version" : "2023-06-01" ,
263+ "content-type" : "application/json" ,
264+ "anthropic-dangerous-direct-browser-access" : "true"
265+ } ,
266+ body : JSON . stringify ( {
267+ model : "claude-3-5-haiku-latest" ,
268+ max_tokens : 4096 ,
269+ system : systemPrompt ,
270+ messages : [
271+ {
272+ role : "user" ,
273+ content : content
274+ }
275+ ]
276+ } )
277+ } ) ;
278+
279+ const data = await response . json ( ) ;
280+ console . log ( JSON . stringify ( data , null , 2 ) ) ;
281+ const markdown = data . content [ 0 ] . text ;
282+
283+ promptRendered . innerHTML = marked . parse ( markdown ) ;
284+ promptResult . style . display = 'block' ;
285+
286+ let responseMarkdown = markdown ;
287+ promptRendered . innerHTML = marked . parse ( responseMarkdown ) ;
288+
289+ promptCopyBtn . onclick = ( ) => {
290+ navigator . clipboard . writeText ( responseMarkdown ) ;
291+ const originalText = promptCopyBtn . textContent ;
292+ promptCopyBtn . textContent = 'Copied' ;
293+ setTimeout ( ( ) => {
294+ promptCopyBtn . textContent = originalText ;
295+ } , 1500 ) ;
296+ } ;
297+
298+ } catch ( error ) {
299+ console . error ( "Error calling Claude API:" , error ) ;
300+ promptRendered . innerHTML = '<p class="error">Error generating summary. Please try again.</p>' ;
301+ } finally {
302+ promptLoading . style . display = 'none' ;
303+ }
304+ } ) ;
181305 </ script >
182306</ body >
183307</ html >
0 commit comments