Skip to content

Commit be05fc3

Browse files
authored
Chrome Prompt Playground
Claude 3.5 Sonnet built most of this - transcript here: https://gist.github.com/simonw/e62440114960bc98f200eb3d92593896
1 parent b3fcfe7 commit be05fc3

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
- [Annotated presentation creator](https://til.simonwillison.net/tools/annotated-presentations) to help turn slides into an annotated presentation
77
- [Arena animated](https://tools.simonwillison.net/arena-animated) animates the progression of the LMSYS Chatbot Arena, inspired by [this visualization](https://public.flourish.studio/visualisation/17992181/) by [Peter Gostev](https://www.linkedin.com/posts/peter-gostev_how-companies-llms-compare-over-the-course-activity-7196899934615257090-zilk) (via [Time-Winter-4319 on Reddit](https://www.reddit.com/r/LocalLLaMA/comments/1bp4j19/gpt4_is_no_longer_the_top_dog_timelapse_of/))
88
- [Compare PDFs](https://tools.simonwillison.net/compare-pdfs) provides a visual comparison of the pages of two PDF files
9+
- [Chrome Prompt Playground](https://tools.simonwillison.net/chrome-prompt-playground) is a UI for running prompts through the Google Chrome Canary experimental Gemini Nano LLM and saving the results in local storage
10+
911

1012
On Observable:
1113

chrome-prompt-playground.html

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Chrome Prompt Playground</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
max-width: 800px;
11+
margin: 0 auto;
12+
padding: 20px;
13+
}
14+
#error-message {
15+
color: darkred;
16+
font-weight: bold;
17+
}
18+
#prompt-area {
19+
display: none;
20+
}
21+
textarea {
22+
width: 100%;
23+
height: 100px;
24+
}
25+
#response-area {
26+
white-space: pre-wrap;
27+
background-color: #f0f0f0;
28+
padding: 10px;
29+
margin-top: 10px;
30+
}
31+
.history-item {
32+
border: 1px solid #ccc;
33+
padding: 10px;
34+
margin-bottom: 10px;
35+
}
36+
.history-item .response {
37+
white-space: pre-wrap;
38+
}
39+
.timestamp {
40+
font-size: 0.8em;
41+
color: #666;
42+
}
43+
label[for="prompt-input"] {
44+
display: block;
45+
margin-bottom: 0.3em;
46+
font-weight: bold;
47+
}
48+
</style>
49+
</head>
50+
<body>
51+
<div id="error-message"></div>
52+
<div id="prompt-area">
53+
<h1>Chrome window.ai prompt playground</h1>
54+
<p>Run prompts against the Gemini Nano experimental model in Chrome Canary.</p>
55+
<label for="prompt-input">Prompt:</label>
56+
<textarea id="prompt-input"></textarea>
57+
<button id="submit-button">Execute prompt</button>
58+
<div id="response-area"></div>
59+
</div>
60+
<div id="history-area"></div>
61+
62+
<script>
63+
document.addEventListener('DOMContentLoaded', () => {
64+
const errorMessage = document.getElementById('error-message');
65+
const promptArea = document.getElementById('prompt-area');
66+
const promptInput = document.getElementById('prompt-input');
67+
const submitButton = document.getElementById('submit-button');
68+
const responseArea = document.getElementById('response-area');
69+
const historyArea = document.getElementById('history-area');
70+
responseArea.style.display = 'none';
71+
72+
if (!window.ai) {
73+
errorMessage.innerHTML = `
74+
<h2>window.ai not found</h2>
75+
<p>Try this in Chrome Canary with "Prompt API for Gemini Nano" enabled in <code>chrome://flags</code></p>
76+
<p>You may also need to wait several hours for the model to download.</p>
77+
`;
78+
return;
79+
}
80+
81+
promptArea.style.display = 'block';
82+
83+
submitButton.addEventListener('click', async () => {
84+
const prompt = promptInput.value.trim();
85+
if (!prompt) return;
86+
responseArea.style.display = 'block';
87+
responseArea.textContent = 'Generating response...';
88+
let fullResponse = '';
89+
90+
try {
91+
const model = await window.ai.createTextSession();
92+
const stream = await model.promptStreaming(prompt);
93+
94+
for await (const chunk of stream) {
95+
fullResponse = chunk;
96+
responseArea.textContent = fullResponse;
97+
}
98+
99+
saveToHistory(prompt, fullResponse);
100+
displayHistory();
101+
} catch (error) {
102+
responseArea.textContent = `Error: ${error.message}`;
103+
}
104+
});
105+
106+
function saveToHistory(prompt, response) {
107+
const history = JSON.parse(localStorage.getItem('aiPromptHistory') || '[]');
108+
history.unshift({
109+
prompt,
110+
response,
111+
timestamp: new Date().toISOString()
112+
});
113+
localStorage.setItem('aiPromptHistory', JSON.stringify(history));
114+
}
115+
116+
function displayHistory() {
117+
const history = JSON.parse(localStorage.getItem('aiPromptHistory') || '[]');
118+
if (!history.length) {
119+
historyArea.innerHTML = '';
120+
return;
121+
}
122+
historyArea.innerHTML = '<h2>History</h2>';
123+
124+
history.forEach((item, index) => {
125+
const historyItem = document.createElement('div');
126+
historyItem.className = 'history-item';
127+
historyItem.innerHTML = `
128+
<p><strong>Prompt:</strong> ${item.prompt}</p>
129+
<p class="response"><strong>Response:</strong> ${item.response}</p>
130+
<p class="timestamp">Timestamp: ${new Date(item.timestamp).toLocaleString()}</p>
131+
<button class="delete-button" data-index="${index}">Delete</button>
132+
`;
133+
historyArea.appendChild(historyItem);
134+
});
135+
136+
document.querySelectorAll('.delete-button').forEach(button => {
137+
button.addEventListener('click', (e) => {
138+
const index = e.target.getAttribute('data-index');
139+
deleteHistoryItem(index);
140+
});
141+
});
142+
}
143+
144+
function deleteHistoryItem(index) {
145+
const history = JSON.parse(localStorage.getItem('aiPromptHistory') || '[]');
146+
history.splice(index, 1);
147+
localStorage.setItem('aiPromptHistory', JSON.stringify(history));
148+
displayHistory();
149+
}
150+
151+
displayHistory();
152+
});
153+
</script>
154+
</body>
155+
</html>

0 commit comments

Comments
 (0)