study-lab/index.html
index.htmlRaw
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">
6 <meta name="color-scheme" content="light dark">
7 <title>Study Pal — Branch your thinking</title>
8 <script>
9 try {
10 document.documentElement.dataset.theme = localStorage.getItem('study-pal:theme') ||
11 (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
12 } catch { document.documentElement.dataset.theme = 'light'; }
13 </script>
14 <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
15 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css">
16 <link rel="stylesheet" href="styles.css">
17</head>
18<body>
19 <header class="topbar">
20 <a class="brand" href="#" aria-label="Study Pal home">
21 <span class="brand-mark" aria-hidden="true">S</span>
22 <span>Study Pal</span>
23 </a>
24 <div class="top-actions">
25 <button id="theme-toggle" class="theme-toggle" type="button" aria-label="Switch to dark theme" title="Switch theme">
26 <span aria-hidden="true">☾</span>
27 </button>
28 <button id="new-session" class="button button-ghost" type="button">New map</button>
29 <button id="open-settings" class="button button-dark" type="button" aria-haspopup="dialog">
30 <span class="status-dot" id="key-status" aria-hidden="true"></span>
31 Settings
32 </button>
33 </div>
34 </header>
35
36 <main class="app-shell">
37 <section class="workspace" aria-label="Conversation">
38 <div id="welcome" class="welcome">
39 <div class="eyebrow">A thinking space that branches</div>
40 <h1>Follow every<br><em>line of thought.</em></h1>
41 <p>Ask a question, explore the answer, then branch from any earlier idea without losing your way.</p>
42 <div class="starter-grid" aria-label="Example questions">
43 <button class="starter" data-prompt="Explain gradient descent visually and mathematically.">
44 <span class="starter-icon">↘</span>
45 <strong>Learn a concept</strong>
46 <small>Explain gradient descent visually</small>
47 </button>
48 <button class="starter" data-prompt="Compare TCP and UDP with practical examples.">
49 <span class="starter-icon">⇄</span>
50 <strong>Compare ideas</strong>
51 <small>When should I use TCP or UDP?</small>
52 </button>
53 <button class="starter" data-prompt="Derive the quadratic formula step by step using LaTeX.">
54 <span class="starter-icon">∑</span>
55 <strong>Work through math</strong>
56 <small>Derive the quadratic formula</small>
57 </button>
58 </div>
59 </div>
60
61 <div id="conversation" class="conversation" aria-live="polite"></div>
62
63 <div class="composer-wrap">
64 <div id="branch-context" class="branch-context" hidden>
65 <span>Branching from <strong id="branch-label"></strong></span>
66 <button id="clear-branch" type="button" aria-label="Return to latest node">×</button>
67 </div>
68 <form id="composer" class="composer">
69 <textarea id="prompt" rows="1" placeholder="Ask anything…" aria-label="Message" maxlength="30000"></textarea>
70 <div class="composer-footer">
71 <span class="hint"><kbd>Enter</kbd> send · <kbd>Shift</kbd> + <kbd>Enter</kbd> new line</span>
72 <button id="send" class="send" type="submit" aria-label="Send message">↑</button>
73 </div>
74 </form>
75 <p class="privacy-note">Your map stays in this browser.</p>
76 </div>
77 </section>
78
79 <aside class="map-panel" aria-label="Concept map">
80 <div class="map-heading">
81 <div>
82 <span class="eyebrow">Your exploration</span>
83 <h2>Concept map</h2>
84 </div>
85 <button id="collapse-map" class="icon-button" type="button" aria-label="Collapse concept map">→</button>
86 </div>
87 <p id="map-help" class="map-help">Select a node to revisit it and start a new branch.</p>
88 <nav id="tree" class="tree" aria-label="Question branches"></nav>
89 <div id="map-empty" class="map-empty">
90 <span class="empty-orbit">✦</span>
91 <p>Your questions will grow into a map here.</p>
92 </div>
93 <div class="map-stats">
94 <span><strong id="node-count">0</strong> nodes</span>
95 <span><strong id="branch-count">0</strong> branches</span>
96 </div>
97 </aside>
98 <button id="expand-map" class="expand-map" type="button" hidden>← Concept map</button>
99 </main>
100
101 <div id="selection-menu" class="selection-menu" role="dialog" aria-label="Ask about selected text" hidden>
102 <div class="selection-topline">
103 <span>Ask about selection</span>
104 <button id="close-selection" type="button" aria-label="Close">×</button>
105 </div>
106 <blockquote id="selection-preview"></blockquote>
107 <select id="selection-action" aria-label="Question type">
108 <option value="Explain this passage in simpler terms.">Explain simply</option>
109 <option value="Go deeper into this idea and its implications.">Go deeper</option>
110 <option value="Give me a concrete example of this.">Give an example</option>
111 <option value="What assumptions does this passage make?">Challenge assumptions</option>
112 <option value="custom">Write my own…</option>
113 </select>
114 <textarea id="selection-custom" rows="2" placeholder="What would you like to ask?" hidden></textarea>
115 <button id="ask-selection" class="button button-dark" type="button">Ask</button>
116 </div>
117
118 <dialog id="settings-dialog" class="settings-dialog">
119 <form id="settings-form" method="dialog">
120 <div class="dialog-heading">
121 <div>
122 <span class="eyebrow">Connection</span>
123 <h2>API settings</h2>
124 </div>
125 <button id="close-settings" class="icon-button" type="button" aria-label="Close settings">×</button>
126 </div>
127 <p class="settings-copy">Use an OpenAI API key (not a ChatGPT password). It is stored only in this browser and sent directly to the configured API.</p>
128 <label>
129 API key
130 <div class="secret-field">
131 <input id="api-key" type="password" placeholder="sk-…" autocomplete="off" spellcheck="false">
132 <button id="toggle-key" type="button" aria-label="Show API key">Show</button>
133 </div>
134 </label>
135 <label>
136 Model
137 <input id="model" type="text" value="gpt-5-mini" placeholder="gpt-5-mini" spellcheck="false">
138 </label>
139 <details>
140 <summary>Advanced</summary>
141 <label>
142 Responses API endpoint
143 <input id="api-url" type="url" value="https://api.openai.com/v1/responses" spellcheck="false">
144 </label>
145 <label>
146 System instructions
147 <textarea id="system-prompt" rows="3">You are a patient technical tutor. Use clear Markdown and LaTeX where useful. Build on the conversation context and explain assumptions.</textarea>
148 </label>
149 </details>
150 <div class="security-callout"><strong>Local-use note</strong> — any script running on this origin can access local storage. Use a restricted project key and never deploy this frontend publicly with a saved key.</div>
151 <div class="dialog-actions">
152 <button id="forget-key" class="button button-ghost danger" type="button">Forget key</button>
153 <button class="button button-dark" value="save" type="submit">Save settings</button>
154 </div>
155 </form>
156 </dialog>
157
158 <div id="toast" class="toast" role="status" aria-live="polite"></div>
159
160 <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"></script>
161 <script defer src="app.js"></script>
162</body>
163</html>