PageSourceSearch

https://labs.davidbauer.ch/apps/sidekick/

html davidbauer.ch collected 2026-10-02 07:22:12 UTC 10,974 bytes, 230 lines download raw bytes

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  <meta name="description" content="A lightweight AI assistant to provide help directly alongside your CMS. Made by David Bauer and Claude for Republik.">
7  
8  <!-- Open Graph / Facebook -->
9  <meta property="og:type" content="website">
10  <meta property="og:title" content="Sidekick – Lightweight AI Assistant (Bookmarklet)">
11  <meta property="og:description" content="A lightweight AI assistant to provide help directly alongside your CMS. Made by David Bauer and Claude for Republik.">
12  <meta property="og:image" content="https://labs.davidbauer.ch/apps/sidekick/social.png">
13  
14  
15  <title>Sidekick – Lightweight AI Assistant for Writers (Bookmarklet)</title>
16  <link rel="icon" type="image/x-icon" href="favicon.ico">
17  
18  <!-- Plausible Analytics -->
19  
19<script defer data-domain="davidbauer.ch" src="https://plausible.io/js/plausible.js"></script>
19
20
21  <link rel="preconnect" href="https://fonts.googleapis.com">
22  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
23  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
24  <link rel="stylesheet" href="styles.css" id="main-stylesheet">
25  
26  <!-- Initialization in init.js -->
27</head>
28<body>
29  <!-- Landing Page -->
30  <div class="landing-page">
31    <!-- Landing Header -->
32    <div class="landing-header">
33      <h1>Sidekick</h1>
34      <p>A lightweight AI assistant for writers that provides help directly alongside your CMS. Made by <a href="https://davidbauer.ch">David Bauer</a> and <a href="https://claude.ai">Claude</a> for <a href="https://www.republik.ch">Republik</a>.</p>
35    </div>
36    
37    <!-- Bookmarklet Section -->
38    <div id="bookmarklet-section">
39      <h2>🔖 Install bookmarklet</h2>
40      <div class="bookmarklet-content">
41        <p style="font-size: 13px; color: #666; margin-bottom: 12px;">
42          Diesen Button in die Bookmarks-Leiste des Browsers ziehen. Dann im Artikeleditor eines unterstützten CMS (Publikator oder Sanity Studio) per Klick aktivieren und nutzen. Keine Inhalte werden für das Training von KI-Modellen genutzt. Bei Fragen: David fragen.
43        </p>
44        <p>
45          <a id="bookmarklet-link" href="#" class="bookmarklet-btn" onclick="return false;">
46            🪄 Sidekick
47          </a>
48        </p>
49      </div>
50      <div class="bookmarklet-overlay" id="bookmarklet-overlay">
51        <p>⚠️ Bookmarklet installation is only available for Republik's CMS.</p>
52      </div>
53    </div>
54   
55    <!-- Testing Section -->
56    <div class="testing-section">
57      <br><br>
58      <h2>See how it works</h2>
59      <p>You can test the bookmarklet's features directly on this page (see that nice blue button in the corner?).</p>
60
61      <div class="config-section">
62        <h2>⚙️ Configuration</h2>
63        <label for="api-key-input">Anthropic API Key</label>
64        <input type="password" id="api-key-input" placeholder="sk-ant-...">
65        <p class="annotation">
66          Get your API key from: <a href="https://console.anthropic.com">console.anthropic.com</a>. Your key stays private: It is stored only in your browser's localStorage (so you don't have to enter it again and again) and is sent directly to Anthropic's API. Per <a href="https://privacy.claude.com/en/articles/7996866-how-long-do-you-store-my-organization-s-data">Anthropic's commercial data policy</a>, API inputs and outputs are not used to train their models and are automatically deleted within 30 days.
67        </p>
68      </div>
69      
70      <!-- MOCK CMS CONTENT -->
71      <div class="mock-cms">
72      <h2>📝 Content to test with</h2>
73      
74      <div class="form-field">
75        <label for="mock-title">Title</label>
76        <input type="text" id="mock-title" name="title" placeholder="">
77      </div>
78      
79      <div class="form-field">
80        <label for="mock-lede">Lead / Short Title</label>
81        <input type="text" id="mock-lede" name="shortTitle" placeholder="">
82      </div>
83      
84      <div class="form-field">
85        <label for="mock-body">Article text</label>
86        <textarea id="mock-body" name="body" placeholder=""></textarea>
87      </div>
88      
89      <div class="form-field">
90        <h2>Example image</h2>
91        <label for="upload-image-1" style="display: block; font-size: 12px; color: #666; margin-bottom: 8px; cursor: pointer;">
92          📤 Upload your own image
93        </label>
94        <input type="file" id="upload-image-1" accept="image/*" style="margin-bottom: 8px; font-size: 13px; font-family: 'JetBrains Mono', monospace;">
95        <img id="example-image-1" src="example-image.png" alt="" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px; margin-top: 8px;">
96        <p style="font-size: 12px; color: #999; margin-top: 4px;">
97          This image has no ALT text and can be tested for ALT text generation.
98        </p>
99      </div>
100      
101      <div class="form-field">
102        <h2>Example chart</h2>
103        <label for="upload-image-2" style="display: block; font-size: 12px; color: #666; margin-bottom: 8px; cursor: pointer;">
104          📤 Upload your own image
105        </label>
106        <input type="file" id="upload-image-2" accept="image/*" style="margin-bottom: 8px; font-size: 13px; font-family: 'JetBrains Mono', monospace;">
107        <img id="example-image-2" src="example-chart.png" alt="" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px; margin-top: 8px;">
108        <p style="font-size: 12px; color: #999; margin-top: 4px;">
109          This chart image has no ALT text and can be tested for ALT text generation.
110        </p>
111      </div>
112      
113     
114      </div>
115    </div>
116  </div>
117
118  <!-- Sidekick UI -->
119  
120  <!-- Floating Action Button & Menu -->
121  <div id="sidekick-overlay">
122    <div id="sidekick-menu">
123      <div id="sidekick-menu-header">SIDEKICK</div>
124      <!-- Menu items generated dynamically from usecases/usecases.json -->
125    </div>
126    <button id="sidekick-toggle" class="sidekick-testing-button" title="Sidekick">
127      🪄 Sidekick
128    </button>
129  </div>
130  
131  <!-- Sidepanel -->
132  <div id="sidekick-sidepanel">
133    <div id="sidekick-panel-warning">
134      ⚠️ Experimental tool – here be dragons
135    </div>
136    <div id="sidekick-panel-header">
137      <div id="sidekick-panel-title-wrapper">
138        <h2 id="sidekick-panel-title">Title suggestions</h2>
139        <p id="sidekick-panel-description"></p>
140      </div>
141      <button id="sidekick-panel-close" title="Close">
142        <svg viewBox="0 0 24 24">
142<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
143      </button>
144    </div>
145    
146    <div id="sidekick-panel-content">
147      <!-- Extracted content preview -->
148      <details id="sidekick-extracted-section">
149        <summary>Show extracted content</summary>
150        <div id="sidekick-extracted-preview"></div>
151      </details>
152      
153      <!-- Prompt editor -->
154      <details id="sidekick-prompt-section">
155        <summary>Edit prompt (not required, but possible)</summary>
156        <textarea id="sidekick-prompt-textarea"></textarea>
157      </details>
158      
159      <!-- Actions -->
160      <div id="sidekick-actions">
161        <button id="sidekick-show-example-btn" class="sidekick-example-btn" style="display: none;">
162          📋 Show Example Response
163        </button>
164        <fieldset id="sidekick-options-fieldset" class="sidekick-options-fieldset" style="display: none;">
165          <legend class="sidekick-options-legend">Check for</legend>
166          <div id="sidekick-options-list" class="sidekick-options-list"></div>
167        </fieldset>
168        <button id="sidekick-send-btn">Send to Claude</button>
169        <p id="sidekick-model-info"></p>
170      </div>
171      
172      <!-- Loading -->
173      <div id="sidekick-loading">
174        <div class="sidekick-spinner"></div>
175        <div id="sidekick-loading-text">Generating suggestions...</div>
176      </div>
177      
178      <!-- Error -->
179      <div id="sidekick-error"></div>
180      
181      <!-- Results -->
182      <div id="sidekick-results" style="display: none;">
183        <div id="sidekick-results-header">Suggestions</div>
184        <div id="sidekick-results-warning">Don't use anything without double-checking. AI is a tool, not a co-author. We bear the responsibility.</div>
185        <div id="sidekick-results-list"></div>
186      </div>
187    </div>
188  </div>
189
190  <!-- Scripts loaded via init.js -->
191  
191<script>
192    // Load placeholder article data
193    (async function() {
194      try {
195        const response = await fetch('placeholder-article.json?v=' + Date.now());
196        if (response.ok) {
197          const placeholder = await response.json();
198          window.PLACEHOLDER_ARTICLE = placeholder;
199          
200          const titleInput = document.getElementById('mock-title');
201          const ledeInput = document.getElementById('mock-lede');
202          const bodyTextarea = document.getElementById('mock-body');
203          
204          if (titleInput) titleInput.placeholder = placeholder.title || '';
205          if (ledeInput) ledeInput.placeholder = placeholder.shortTitle || '';
206          if (bodyTextarea) bodyTextarea.placeholder = placeholder.body || '';
207        }
208      } catch (err) {
209        console.warn('Could not load placeholder article:', err);
210      }
211    })();
212  </script>
212
213  
213<script>
214    (function() {
215      var s = document.createElement('script');
216      s.src = 'init.js' + (['localhost','127.0.0.1',''].includes(location.hostname) ? '?v='+Date.now() : '');
217      document.body.appendChild(s);
218    })();
219  </script>
219
220  
221  <!-- Footer -->
222  <footer class="site-footer">
223    <div class="footer-content">
224      <p>Sidekick is inspired by a similar tool made by <a href="https://www.linkedin.com/in/382462100/">Nico Scordialo</a> at ZEIT. <a href="https://timogrossenbacher.ch/">Timo Grossenbacher</a> and <a href="https://www.linkedin.com/in/beni-buess/">Beni Buess</a> have provided valuable feedback during development.</p>
225      <p>All prompts take inspiration from fellow tinkerers with these kinds of use cases. They include, among many more, <a href="https://www.linkedin.com/in/anne-kathrin-gerstlauer/">Anne-Kathrin Gerstlauer</a>, <a href="https://tk.gg/posts/ai-alt-text">Matt Taylor</a>, <a href="https://olereissmann.com/">Ole Reissmann</a>, <a href="https://lewtan.dev/">Devin Lewtan</a>, <a href="https://www.linkedin.com/in/pae-blue/">Patrick Venetz</a>, <a href="https://www.linkedin.com/in/martin-schori-aftonbladet/">Martin Schori</a>, <a href="https://lex.page/?via=david">Lex</a>.</p>
226      <p>Relevant context: <a href="https://www.republik.ch/2024/09/04/wie-die-republik-zu-kuenstlicher-intelligenz-steht">Wie die Republik zu künstlicher Intelligenz steht</a> | <a href="https://labs.davidbauer.ch/how-i-use-ai/">How I Use AI</a></p>
227    </div>
228  </footer>
229</body>
230</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.