Fix note-field space bar hijacking video, rework Outline topics as documents
- Lesson page's global keyboard shortcuts (space/arrows for play-pause/ seek/volume) fired regardless of focus, so typing a space in the notes textarea toggled the video instead of typing. Now skipped entirely whenever a text field/select has focus. - Outline topics no longer spawn their own top-level collection each time. A topic is now a document inside one fixed, user-configured collection (Settings -> Outline Integration -> Default collection), and a lesson's note becomes a child document nested under its topic - matching how notes actually get organized in a real Outline instance instead of cluttering the collections list with one per topic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -606,11 +606,11 @@
|
||||
const topicStatus = document.getElementById('outline-topic-status');
|
||||
|
||||
if (topicSelect) {
|
||||
fetch('/api/outline/collections')
|
||||
fetch('/api/outline/topics')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const newOption = topicSelect.querySelector('option[value="__new__"]');
|
||||
(data.collections || []).forEach(function(c) {
|
||||
(data.topics || []).forEach(function(c) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = c.id;
|
||||
opt.textContent = c.name;
|
||||
@@ -624,7 +624,7 @@
|
||||
newTopicInput.value = INITIAL_TOPIC_NAME;
|
||||
}
|
||||
if (data.error && topicStatus) {
|
||||
topicStatus.textContent = 'Outline not reachable - topic list may be incomplete';
|
||||
topicStatus.textContent = data.error;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
@@ -805,8 +805,17 @@
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
// Keyboard shortcuts - skip while typing in the note (or any
|
||||
// other text field), so space/arrow keys type normally instead
|
||||
// of hijacking video playback.
|
||||
function isTypingTarget(el) {
|
||||
if (!el) return false;
|
||||
const tag = el.tagName;
|
||||
return tag === 'TEXTAREA' || tag === 'INPUT' || tag === 'SELECT' || el.isContentEditable;
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (isTypingTarget(e.target)) return;
|
||||
if (activeMedia && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||
switch(e.key) {
|
||||
case ' ':
|
||||
|
||||
+37
-3
@@ -524,8 +524,9 @@
|
||||
<div class="card">
|
||||
<h2>Outline Integration</h2>
|
||||
<p class="setting-desc" style="margin-bottom: 12px;">
|
||||
Pick a topic on a lesson's note (on the lesson page) to push it to that topic's collection in
|
||||
Outline when you leave the page. Notes without a topic stay local only.
|
||||
Pick a topic on a lesson's note (on the lesson page) to push it there when you leave the page -
|
||||
each topic becomes a document inside the collection below, and the note is nested under it.
|
||||
Notes without a topic stay local only.
|
||||
</p>
|
||||
<div class="setting-row" style="flex-direction: column; align-items: stretch; gap: 8px;">
|
||||
<label for="outline-url">Outline URL
|
||||
@@ -536,6 +537,12 @@
|
||||
<label for="outline-token" style="margin-top: 4px;">API token</label>
|
||||
<input type="password" id="outline-token" class="hex-input" style="width: 100%; font-family: var(--font-family);"
|
||||
placeholder="Leave blank to keep the current token">
|
||||
<label for="outline-collection" style="margin-top: 4px;">Default collection
|
||||
<span class="setting-desc">Where topic documents get created - e.g. "Training Notes"</span>
|
||||
</label>
|
||||
<select id="outline-collection">
|
||||
<option value="">Save your URL/token first, then pick one</option>
|
||||
</select>
|
||||
<div style="display: flex; gap: 10px; margin-top: 4px;">
|
||||
<button class="btn" onclick="saveOutlineConfig()">Save</button>
|
||||
<button class="btn btn-secondary" onclick="testOutlineConnection()">Test Connection</button>
|
||||
@@ -1002,6 +1009,25 @@
|
||||
document.getElementById('outline-url').value = data.base_url || '';
|
||||
document.getElementById('outline-config-status').textContent =
|
||||
data.configured ? 'Configured' : 'Not configured';
|
||||
loadOutlineCollectionsDropdown(data.default_collection_id || '');
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function loadOutlineCollectionsDropdown(selectedId) {
|
||||
const select = document.getElementById('outline-collection');
|
||||
fetch('/api/outline/collections')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const collections = data.collections || [];
|
||||
if (collections.length === 0) {
|
||||
select.innerHTML = '<option value="">No collections found - check your URL/token</option>';
|
||||
return;
|
||||
}
|
||||
select.innerHTML = collections.map(c =>
|
||||
`<option value="${c.id}">${c.name}</option>`
|
||||
).join('');
|
||||
if (selectedId) select.value = selectedId;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -1010,12 +1036,20 @@
|
||||
const status = document.getElementById('outline-test-status');
|
||||
const url = document.getElementById('outline-url').value.trim();
|
||||
const token = document.getElementById('outline-token').value.trim();
|
||||
const collectionSelect = document.getElementById('outline-collection');
|
||||
const collectionId = collectionSelect.value;
|
||||
const collectionName = collectionId ? collectionSelect.options[collectionSelect.selectedIndex].text : '';
|
||||
status.style.color = 'var(--text-muted)';
|
||||
status.textContent = 'Saving...';
|
||||
fetch('/api/outline/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ base_url: url, api_token: token })
|
||||
body: JSON.stringify({
|
||||
base_url: url,
|
||||
api_token: token,
|
||||
default_collection_id: collectionId,
|
||||
default_collection_name: collectionName
|
||||
})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
|
||||
Reference in New Issue
Block a user