arnaucube
arnaucube/study-labPublic Self-hosted repository
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>