personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

convey: lode 2 canon sweep — D4 mixed-case across templates + deferred surveillance vocab

Apply the system-anatomy + voice-terminology brand canon to the remaining
owner-facing visible strings in the convey UI. Continues the lode-1 sweep
(d95795b2) by lowercasing button text, headings, modal titles, summaries,
labels, placeholders, ARIA labels, titles, alts, and JS-set DOM strings
across all owner-facing templates and adjacent route/CLI surfaces.

- Sweep workspace.html templates for the 19 in-use apps plus shared convey
templates (chat_bar, app, pairing, date_nav). Lowercase static visible
text in headings, buttons, modals, labels, attributes, and JS-set DOM
strings; preserve third-party brand names (Anthropic, OpenAI, Google AI,
Gemini, Plaud, Granola, Obsidian, Vertex, Rev.ai, iPhone), acronyms (API,
JSON, URL, LAN, R2, DMG, ICNS, SVG, STT, WebRTC, QR, AKAs, OK, PDF,
America/New_York), chart day/month abbreviations, and dynamic data
rendered through escapeHtml or template interpolation.

- Handle the surveillance-vocab strings that lode 1 explicitly deferred:
apps/health/workspace.html status lines render "observing" instead of
"Capturing"/"Recording"; apps/transcripts empty-state reads "no
observations were found for this day"; apps/sol/workspace.html log
empty-state drops "recorded" from "no log events for this run"; the
convey/apps.py first-daily chat-bar placeholder reads "observing — your
first daily analysis will be ready soon...".

- Adjacent owner-facing surfaces beyond workspace.html: apps/import/routes.py
SOURCE_METADATA dict (recording → meeting audio, observations);
apps/sol/routes.py JSON API error strings ("talent run ..."); apps/skills/
call.py CLI confirmation messages.

- Sweep <a>, <span>, <div> visible-text strings overlooked by the initial
D4 regex (anchor-styled buttons, status badges, empty-state copy).

- Update three test fixtures whose assertions checked the pre-canon copy:
tests/test_app_reflections.py (Copy → copy, Download PDF → download PDF),
tests/test_pairing_ui.py (Pair a phone → pair a phone),
tests/test_apps_skills_call.py (already recorded → already observed),
tests/test_convey_apps.py (Capture is running → observing).

Line-level edits only; no layout, component, or behavior changes. Code-side
identifiers (CSS class names, JS variables, JSON keys, route paths,
event names, recorded_at field names) untouched. Skipped per scope:
apps/activities/_dev_screens_*.html (dev-only views), convey/copy.py
(already canon-clean), tests/baselines/api/** (backend, not UI),
LLM-prompt content (apps/todos/routes.py prompt template — separate
request).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+661 -661
+4 -4
apps/activities/_day.html
··· 615 615 const ac = activities.length; 616 616 617 617 if (!ac) { 618 - el.textContent = 'no recorded activity'; 618 + el.textContent = 'no observed activity'; 619 619 el.style.display = ''; 620 620 return; 621 621 } ··· 655 655 function renderTimeline(list) { 656 656 const div = document.getElementById('timelineView'); 657 657 if (!list.length) { 658 - div.innerHTML = '<div class="timeline-empty"><div class="empty-icon"><svg width="32" height="32" viewBox="0 0 32 32" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="24" height="22" rx="3"/><line x1="10" y1="3" x2="10" y2="9"/><line x1="22" y1="3" x2="22" y2="9"/><line x1="4" y1="14" x2="28" y2="14"/><line x1="11" y1="19" x2="11" y2="19.01"/><line x1="16" y1="19" x2="16" y2="19.01"/><line x1="21" y1="19" x2="21" y2="19.01"/><line x1="11" y1="24" x2="11" y2="24.01"/><line x1="16" y1="24" x2="16" y2="24.01"/></svg></div><div class="empty-heading">no activity</div><div class="empty-desc">nothing recorded for this day</div></div>'; 658 + div.innerHTML = '<div class="timeline-empty"><div class="empty-icon"><svg width="32" height="32" viewBox="0 0 32 32" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="24" height="22" rx="3"/><line x1="10" y1="3" x2="10" y2="9"/><line x1="22" y1="3" x2="22" y2="9"/><line x1="4" y1="14" x2="28" y2="14"/><line x1="11" y1="19" x2="11" y2="19.01"/><line x1="16" y1="19" x2="16" y2="19.01"/><line x1="21" y1="19" x2="21" y2="19.01"/><line x1="11" y1="24" x2="11" y2="24.01"/><line x1="16" y1="24" x2="16" y2="24.01"/></svg></div><div class="empty-heading">no activity</div><div class="empty-desc">nothing observed for this day</div></div>'; 659 659 return; 660 660 } 661 661 ··· 764 764 : ''; 765 765 766 766 let html = ''; 767 - html += `<button type="button" class="activity-detail-back">← Back to day</button>`; 767 + html += `<button type="button" class="activity-detail-back">← back to day</button>`; 768 768 html += `<div class="activity-detail-header">`; 769 769 html += `<h2>${escapeHtml(a.icon || '')} ${escapeHtml(a.name)}</h2>`; 770 770 html += `<div class="ad-subtitle">${escapeHtml(a.facet)}</div>`; ··· 772 772 773 773 // Build tabs 774 774 html += `<div class="ad-tabs">`; 775 - html += `<button type="button" class="ad-tab active" data-tab="summary">Summary</button>`; 775 + html += `<button type="button" class="ad-tab active" data-tab="summary">summary</button>`; 776 776 a.outputs.forEach((out, i) => { 777 777 const label = out.filename.replace(/\.\w+$/, '').replace(/[_-]/g, ' '); 778 778 html += `<button type="button" class="ad-tab" data-tab="output-${i}">${escapeHtml(label)}</button>`;
+2 -2
apps/chat/workspace.html
··· 27 27 {% if day == today_day %} 28 28 <form id="chatComposerForm" class="chat-composer"> 29 29 <textarea id="chatComposerInput" rows="1" placeholder="{{ chat_bar_placeholder }}" aria-label="chat input"></textarea> 30 - <button id="chatComposerSend" type="submit" aria-label="send">Send</button> 30 + <button id="chatComposerSend" type="submit" aria-label="send">send</button> 31 31 </form> 32 32 {% else %} 33 33 <p class="chat-composer-past">past-day view — new messages go to <a href="{{ url_for('app:chat.day', day=today_day) }}">today</a></p> ··· 339 339 note.className = 'chat-talent-card-stalled-note'; 340 340 card.appendChild(note); 341 341 } 342 - note.textContent = 'Talent stopped responding — reload to retry'; 342 + note.textContent = 'talent stopped responding — reload to retry'; 343 343 }); 344 344 } 345 345
+73 -73
apps/entities/workspace.html
··· 1306 1306 <div id="entities-loading" class="entities-loading"> 1307 1307 <div class="surface-state surface-state--loading" role="status" aria-busy="true"> 1308 1308 <div class="surface-state-spinner spinner" aria-hidden="true"></div> 1309 - <span class="surface-state-text" data-role="loading-status">Loading entities...</span> 1309 + <span class="surface-state-text" data-role="loading-status">loading entities...</span> 1310 1310 </div> 1311 1311 </div> 1312 1312 ··· 1315 1315 <div class="entities-section"> 1316 1316 <h3> 1317 1317 <span>your entities</span> 1318 - <input type="text" id="journal-entity-card-search" class="entity-search-input" placeholder="search entities..." aria-label="Search entity cards"> 1318 + <input type="text" id="journal-entity-card-search" class="entity-search-input" placeholder="search entities..." aria-label="search entity cards"> 1319 1319 <span id="journal-card-search-count" class="entity-card-search-count" style="display: none;"></span> 1320 1320 </h3> 1321 1321 <p class="entities-subtitle">people, places, and things across your journal</p> 1322 1322 <div class="entity-cards-container"> 1323 1323 <div id="journal-entity-cards-content"></div> 1324 1324 <div id="no-journal-entities" class="no-journal-entities" style="display: none;"> 1325 - <h2>No entities yet</h2> 1325 + <h2>no entities yet</h2> 1326 1326 <p>Select a facet to add entities to your journal.</p> 1327 1327 </div> 1328 1328 <div id="no-journal-card-matches" class="no-card-matches" style="display: none;">No entities match your search.</div> ··· 1333 1333 <!-- Journal entity detail view (all-facet mode) --> 1334 1334 <div id="journal-entity-detail-view" class="entity-detail-view" style="display: none;"> 1335 1335 <div class="entity-detail-header"> 1336 - <a class="entity-detail-back" href="#" onclick="event.preventDefault(); navigateToList()">← Back to entities</a> 1336 + <a class="entity-detail-back" href="#" onclick="event.preventDefault(); navigateToList()">← back to entities</a> 1337 1337 <div class="entity-detail-title"> 1338 1338 <h2 class="entity-detail-name" id="journal-detail-name"></h2> 1339 1339 <span class="entity-detail-type" id="journal-detail-type"></span> ··· 1352 1352 <div id="journal-detail-edit-error" class="error-message" style="display: none;"></div> 1353 1353 <form class="entity-edit-form" id="journal-detail-edit-form"> 1354 1354 <div class="form-group"> 1355 - <label for="journal-detail-edit-name">Name</label> 1355 + <label for="journal-detail-edit-name">name</label> 1356 1356 <input type="text" id="journal-detail-edit-name" required> 1357 1357 </div> 1358 1358 <div class="form-group"> 1359 - <label for="journal-detail-edit-type">Type</label> 1359 + <label for="journal-detail-edit-type">type</label> 1360 1360 <select id="journal-detail-edit-type"> 1361 1361 <!-- Populated by loadEntityTypes() --> 1362 1362 </select> ··· 1364 1364 <div class="form-group"> 1365 1365 <label for="journal-detail-edit-akas">alternative names (AKAs)</label> 1366 1366 <input type="text" id="journal-detail-edit-akas" placeholder="alias1, alias2, alias3"> 1367 - <small>Comma-separated list of alternative names for audio transcription</small> 1367 + <small>comma-separated list of alternative names for audio transcription</small> 1368 1368 </div> 1369 1369 <div class="form-actions"> 1370 1370 <button type="submit" class="btn btn-primary" id="journal-detail-save-btn">save changes</button> ··· 1374 1374 1375 1375 <div class="entity-action-section entity-detach-section"> 1376 1376 <div style="display: flex; gap: 0.5em; flex-wrap: wrap;"> 1377 - <button class="btn btn-secondary" id="journal-detail-block-btn">Block</button> 1378 - <button class="btn btn-danger" id="journal-detail-delete-btn">Delete</button> 1377 + <button class="btn btn-secondary" id="journal-detail-block-btn">block</button> 1378 + <button class="btn btn-danger" id="journal-detail-delete-btn">delete</button> 1379 1379 </div> 1380 1380 <div id="block-action-error" class="error-message" style="display: none;"></div> 1381 1381 <p style="margin-top: 0.75em; font-size: 0.85em; color: #6c757d;"> 1382 - <strong>Block</strong> hides the entity from all views but preserves data. 1383 - <strong>Delete</strong> permanently removes the entity and all associated data. 1382 + <strong>block</strong> hides the entity from all views but preserves data. 1383 + <strong>delete</strong> permanently removes the entity and all associated data. 1384 1384 </p> 1385 1385 </div> 1386 1386 </div> ··· 1391 1391 <div class="entities-section"> 1392 1392 <h3 id="facet-entities-heading"> 1393 1393 <span>entities</span> 1394 - <input type="text" id="entity-card-search" class="entity-search-input" placeholder="search entities..." aria-label="Search entity cards"> 1394 + <input type="text" id="entity-card-search" class="entity-search-input" placeholder="search entities..." aria-label="search entity cards"> 1395 1395 <span id="facet-card-search-count" class="entity-card-search-count" style="display: none;"></span> 1396 1396 </h3> 1397 1397 <p class="entities-subtitle" id="facet-entities-subtitle">people, places, and things you're tending</p> 1398 1398 <div class="entity-cards-container"> 1399 1399 <div id="entity-cards-content"></div> 1400 1400 <div id="no-facet-entities" class="no-entities" style="display: none;"> 1401 - No entities added to this facet yet. Star entities below to add them. 1401 + no entities added to this facet yet. star entities below to add them. 1402 1402 </div> 1403 - <div id="no-facet-card-matches" class="no-card-matches" style="display: none;">No entities match your search.</div> 1403 + <div id="no-facet-card-matches" class="no-card-matches" style="display: none;">no entities match your search.</div> 1404 1404 </div> 1405 1405 </div> 1406 1406 ··· 1409 1409 <div class="entities-container"> 1410 1410 <h3 id="all-entities-title"> 1411 1411 <span>detected entities</span> 1412 - <input type="text" id="entity-search" class="entity-search-input" placeholder="search entities..." aria-label="Search detected entities"> 1412 + <input type="text" id="entity-search" class="entity-search-input" placeholder="search entities..." aria-label="search detected entities"> 1413 1413 </h3> 1414 1414 <p class="entities-subtitle">found by AI in the last 30 days — star to add to your journal</p> 1415 1415 <div id="detected-action-error" class="error-message" style="display: none;"></div> ··· 1433 1433 <!-- Entity detail view --> 1434 1434 <div id="entity-detail-view" class="entity-detail-view" style="display: none;"> 1435 1435 <div class="entity-detail-header"> 1436 - <a class="entity-detail-back" href="#" onclick="event.preventDefault(); navigateToList()">← Back to entities</a> 1436 + <a class="entity-detail-back" href="#" onclick="event.preventDefault(); navigateToList()">← back to entities</a> 1437 1437 <div class="entity-detail-title"> 1438 1438 <h2 class="entity-detail-name" id="detail-name"></h2> 1439 1439 <span class="entity-detail-type" id="detail-type"></span> ··· 1461 1461 <div class="entity-attach-section" id="detail-attach-section" style="display: none;"> 1462 1462 <h4>attach to <span id="attach-facet-name"></span></h4> 1463 1463 <label class="attach-description-label"> 1464 - Description <span class="required">*</span> 1465 - <small style="font-weight: normal; color: #6c757d;">(How does this entity relate to this facet?)</small> 1464 + description <span class="required">*</span> 1465 + <small style="font-weight: normal; color: #6c757d;">(how does this entity relate to this facet?)</small> 1466 1466 </label> 1467 - <textarea id="attach-description" placeholder="e.g., Lead engineer on the project, Primary contact for sales..."></textarea> 1468 - <button class="btn-attach" id="attach-entity-btn" disabled>Attach to Facet</button> 1467 + <textarea id="attach-description" placeholder="e.g., lead engineer on the project, primary contact for sales..."></textarea> 1468 + <button class="btn-attach" id="attach-entity-btn" disabled>attach to facet</button> 1469 1469 <div id="attach-error" style="color: #dc3545; margin-top: 0.5em; display: none;"></div> 1470 1470 </div> 1471 1471 </div> ··· 1476 1476 <div class="modal-content"> 1477 1477 <div class="modal-header danger"> 1478 1478 <h3>delete detected entity</h3> 1479 - <button class="close" aria-label="Close" onclick="closeDeleteModal()">&times;</button> 1479 + <button class="close" aria-label="close" onclick="closeDeleteModal()">&times;</button> 1480 1480 </div> 1481 1481 <div class="modal-body"> 1482 1482 <div class="delete-warning"> 1483 - ⚠️ This will permanently remove this entity from all detected day files. This action cannot be undone. 1483 + ⚠️ this will permanently remove this entity from all detected day files. this action cannot be undone. 1484 1484 </div> 1485 - <p>Entity: <strong id="deleteEntityName"></strong></p> 1485 + <p>entity: <strong id="deleteEntityName"></strong></p> 1486 1486 <div id="deleteDaysContainer" style="display: none;"> 1487 - <p>Found in <strong id="deleteDaysCount"></strong>:</p> 1487 + <p>found in <strong id="deleteDaysCount"></strong>:</p> 1488 1488 <div class="delete-days-list" id="deleteDaysList"></div> 1489 1489 </div> 1490 1490 </div> 1491 1491 <div class="modal-footer"> 1492 - <button class="btn btn-secondary" onclick="closeDeleteModal()">Cancel</button> 1493 - <button id="confirmDeleteBtn" class="btn btn-danger" onclick="confirmEntityDelete()">Delete</button> 1492 + <button class="btn btn-secondary" onclick="closeDeleteModal()">cancel</button> 1493 + <button id="confirmDeleteBtn" class="btn btn-danger" onclick="confirmEntityDelete()">delete</button> 1494 1494 </div> 1495 1495 </div> 1496 1496 </div> ··· 1500 1500 <div class="modal-content"> 1501 1501 <div class="modal-header danger"> 1502 1502 <h3>delete entity permanently</h3> 1503 - <button class="close" aria-label="Close" onclick="closeDeleteJournalEntityModal()">&times;</button> 1503 + <button class="close" aria-label="close" onclick="closeDeleteJournalEntityModal()">&times;</button> 1504 1504 </div> 1505 1505 <div class="modal-body"> 1506 1506 <div class="delete-warning"> 1507 - ⚠️ This will permanently delete this entity and all associated data. This action cannot be undone. 1507 + ⚠️ this will permanently delete this entity and all associated data. this action cannot be undone. 1508 1508 </div> 1509 - <p>Entity: <strong id="deleteJournalEntityName"></strong></p> 1509 + <p>entity: <strong id="deleteJournalEntityName"></strong></p> 1510 1510 <div id="deleteJournalEntityInfo"> 1511 - <p>This will remove:</p> 1511 + <p>this will remove:</p> 1512 1512 <ul style="margin: 0.5em 0; padding-left: 1.5em; color: #495057;"> 1513 - <li>The entity record</li> 1513 + <li>the entity record</li> 1514 1514 <li id="deleteJournalEntityFacets"></li> 1515 1515 <li id="deleteJournalEntityObs"></li> 1516 1516 </ul> ··· 1518 1518 <div id="delete-modal-error" class="error-message" style="display: none; margin-top: 0.5em;"></div> 1519 1519 </div> 1520 1520 <div class="modal-footer"> 1521 - <button class="btn btn-secondary" onclick="closeDeleteJournalEntityModal()">Cancel</button> 1521 + <button class="btn btn-secondary" onclick="closeDeleteJournalEntityModal()">cancel</button> 1522 1522 <button id="confirmDeleteJournalEntityBtn" class="btn btn-danger" onclick="confirmDeleteJournalEntity()">delete permanently</button> 1523 1523 </div> 1524 1524 </div> ··· 1730 1730 1731 1731 function showFacetDetailView(entityId) { 1732 1732 // Show loading state 1733 - document.getElementById('detail-name').textContent = 'Loading...'; 1733 + document.getElementById('detail-name').textContent = 'loading...'; 1734 1734 document.getElementById('detail-type').textContent = ''; 1735 1735 document.getElementById('detail-aka').textContent = ''; 1736 1736 document.getElementById('detail-indicators').innerHTML = ''; 1737 - document.getElementById('detail-description-container').innerHTML = '<span class="entity-desc-display empty">Loading...</span>'; 1738 - document.getElementById('detail-observations').innerHTML = '<span class="observations-empty">Loading...</span>'; 1737 + document.getElementById('detail-description-container').innerHTML = '<span class="entity-desc-display empty">loading...</span>'; 1738 + document.getElementById('detail-observations').innerHTML = '<span class="observations-empty">loading...</span>'; 1739 1739 1740 1740 // Fetch entity details 1741 1741 fetch(`api/${encodeURIComponent(currentFacet)}/entity/${encodeURIComponent(entityId)}`) ··· 1749 1749 }) 1750 1750 .catch(error => { 1751 1751 console.error('Error loading entity:', error); 1752 - document.getElementById('detail-name').textContent = 'Error'; 1752 + document.getElementById('detail-name').textContent = 'error'; 1753 1753 document.getElementById('detail-description-container').innerHTML = 1754 1754 `<div class="error-message">${window.AppServices.escapeHtml(error.message)}</div>`; 1755 1755 }); ··· 1757 1757 1758 1758 function showJournalDetailView(entityId) { 1759 1759 // Show loading state 1760 - document.getElementById('journal-detail-name').textContent = 'Loading...'; 1760 + document.getElementById('journal-detail-name').textContent = 'loading...'; 1761 1761 document.getElementById('journal-detail-type').textContent = ''; 1762 1762 document.getElementById('journal-detail-aka').textContent = ''; 1763 1763 document.getElementById('journal-detail-indicators').innerHTML = ''; 1764 - document.getElementById('journal-detail-facets').innerHTML = '<span class="observations-empty">Loading...</span>'; 1764 + document.getElementById('journal-detail-facets').innerHTML = '<span class="observations-empty">loading...</span>'; 1765 1765 1766 1766 // Fetch journal entity details 1767 1767 fetch(`api/journal/entity/${encodeURIComponent(entityId)}`) ··· 1775 1775 }) 1776 1776 .catch(error => { 1777 1777 console.error('Error loading journal entity:', error); 1778 - document.getElementById('journal-detail-name').textContent = 'Error'; 1778 + document.getElementById('journal-detail-name').textContent = 'error'; 1779 1779 document.getElementById('journal-detail-facets').innerHTML = 1780 1780 `<div class="error-message">${window.AppServices.escapeHtml(error.message)}</div>`; 1781 1781 }); ··· 1806 1806 principalBadge.id = 'journal-detail-principal-badge'; 1807 1807 principalBadge.className = 'entity-detail-principal-badge'; 1808 1808 principalBadge.textContent = 'you'; 1809 - principalBadge.title = 'This is you'; 1809 + principalBadge.title = 'this is you'; 1810 1810 typeEl.parentNode.insertBefore(principalBadge, typeEl.nextSibling); 1811 1811 } 1812 1812 } else if (principalBadge) { ··· 1820 1820 blockedBadge = document.createElement('span'); 1821 1821 blockedBadge.id = 'journal-detail-blocked-badge'; 1822 1822 blockedBadge.className = 'entity-detail-blocked-badge'; 1823 - blockedBadge.textContent = 'Blocked'; 1824 - blockedBadge.title = 'This entity is blocked'; 1823 + blockedBadge.textContent = 'blocked'; 1824 + blockedBadge.title = 'this entity is blocked'; 1825 1825 typeEl.parentNode.insertBefore(blockedBadge, typeEl.nextSibling); 1826 1826 } 1827 1827 } else if (blockedBadge) { ··· 1835 1835 if (isPrincipal) { 1836 1836 // Principal cannot be blocked or deleted 1837 1837 blockBtn.disabled = true; 1838 - blockBtn.textContent = 'Block'; 1839 - blockBtn.title = 'Cannot block yourself'; 1838 + blockBtn.textContent = 'block'; 1839 + blockBtn.title = 'cannot block yourself'; 1840 1840 blockBtn.onclick = null; 1841 1841 deleteBtn.disabled = true; 1842 - deleteBtn.title = 'Cannot delete yourself'; 1842 + deleteBtn.title = 'cannot delete yourself'; 1843 1843 deleteBtn.onclick = null; 1844 1844 } else if (isBlocked) { 1845 1845 // Show unblock option 1846 1846 blockBtn.disabled = false; 1847 - blockBtn.textContent = 'Unblock'; 1848 - blockBtn.title = 'Unblock this entity'; 1847 + blockBtn.textContent = 'unblock'; 1848 + blockBtn.title = 'unblock this entity'; 1849 1849 blockBtn.onclick = unblockJournalEntity; 1850 1850 deleteBtn.disabled = false; 1851 1851 deleteBtn.title = ''; ··· 1853 1853 } else { 1854 1854 // Normal state 1855 1855 blockBtn.disabled = false; 1856 - blockBtn.textContent = 'Block'; 1856 + blockBtn.textContent = 'block'; 1857 1857 blockBtn.title = ''; 1858 1858 blockBtn.onclick = blockJournalEntity; 1859 1859 deleteBtn.disabled = false; ··· 1888 1888 const facets = entity.facets || []; 1889 1889 1890 1890 if (facets.length === 0) { 1891 - facetsContainer.innerHTML = '<div class="no-facet-relationships">Not attached to any facets yet.</div>'; 1891 + facetsContainer.innerHTML = '<div class="no-facet-relationships">not attached to any facets yet.</div>'; 1892 1892 } else { 1893 1893 facetsContainer.innerHTML = '<div class="facet-relationships-list"></div>'; 1894 1894 const list = facetsContainer.querySelector('.facet-relationships-list'); ··· 2016 2016 const saveBtn = document.getElementById('journal-detail-save-btn'); 2017 2017 2018 2018 if (!name) { 2019 - errorEl.textContent = 'Name is required'; 2019 + errorEl.textContent = 'name is required'; 2020 2020 errorEl.style.display = 'block'; 2021 2021 return; 2022 2022 } 2023 2023 2024 2024 saveBtn.disabled = true; 2025 - saveBtn.textContent = 'Saving...'; 2025 + saveBtn.textContent = 'saving...'; 2026 2026 2027 2027 fetch(`api/journal/entity/${encodeURIComponent(entityId)}`, { 2028 2028 method: 'PUT', ··· 2047 2047 document.getElementById('journal-detail-type').textContent = type; 2048 2048 const akaEl = document.getElementById('journal-detail-aka'); 2049 2049 if (aka.length > 0) { 2050 - akaEl.textContent = `Also known as: ${aka.join(', ')}`; 2050 + akaEl.textContent = `also known as: ${aka.join(', ')}`; 2051 2051 } else { 2052 2052 akaEl.textContent = ''; 2053 2053 } ··· 2063 2063 }) 2064 2064 .catch(error => { 2065 2065 console.error('Error updating journal entity:', error); 2066 - errorEl.textContent = error.message || 'Failed to save changes'; 2066 + errorEl.textContent = error.message || 'failed to save changes'; 2067 2067 errorEl.style.display = 'block'; 2068 2068 saveBtn.textContent = 'save changes'; 2069 2069 saveBtn.disabled = false; ··· 2077 2077 2078 2078 const btn = document.getElementById('journal-detail-block-btn'); 2079 2079 btn.disabled = true; 2080 - btn.textContent = 'Blocking...'; 2080 + btn.textContent = 'blocking...'; 2081 2081 2082 2082 fetch(`api/journal/entity/${encodeURIComponent(entityId)}/block`, { 2083 2083 method: 'POST' ··· 2096 2096 console.error('Error blocking entity:', error); 2097 2097 showInlineError('block-action-error', "couldn't block entity"); 2098 2098 btn.disabled = false; 2099 - btn.textContent = 'Block'; 2099 + btn.textContent = 'block'; 2100 2100 }); 2101 2101 } 2102 2102 ··· 2106 2106 2107 2107 const btn = document.getElementById('journal-detail-block-btn'); 2108 2108 btn.disabled = true; 2109 - btn.textContent = 'Unblocking...'; 2109 + btn.textContent = 'unblocking...'; 2110 2110 2111 2111 fetch(`api/journal/entity/${encodeURIComponent(entityId)}/unblock`, { 2112 2112 method: 'POST' ··· 2125 2125 console.error('Error unblocking entity:', error); 2126 2126 showInlineError('block-action-error', "couldn't unblock entity"); 2127 2127 btn.disabled = false; 2128 - btn.textContent = 'Unblock'; 2128 + btn.textContent = 'unblock'; 2129 2129 }); 2130 2130 } 2131 2131 ··· 2161 2161 2162 2162 const btn = document.getElementById('confirmDeleteJournalEntityBtn'); 2163 2163 btn.disabled = true; 2164 - btn.textContent = 'Deleting...'; 2164 + btn.textContent = 'deleting...'; 2165 2165 2166 2166 fetch(`api/journal/entity/${encodeURIComponent(entityId)}`, { 2167 2167 method: 'DELETE' ··· 2346 2346 list.appendChild(item); 2347 2347 }); 2348 2348 } else { 2349 - obsContainer.innerHTML = '<span class="observations-empty">No observations recorded yet.</span>'; 2349 + obsContainer.innerHTML = '<span class="observations-empty">no observations yet.</span>'; 2350 2350 } 2351 2351 2352 2352 // Detach/Re-attach button or Attach section ··· 2383 2383 attachSection.style.display = 'none'; 2384 2384 2385 2385 if (isDetached) { 2386 - detachBtn.textContent = 'Re-attach'; 2386 + detachBtn.textContent = 're-attach'; 2387 2387 detachBtn.className = 'btn btn-primary'; 2388 2388 detachBtn.onclick = () => reattachEntity(entity); 2389 2389 } else { 2390 - detachBtn.textContent = 'Detach'; 2390 + detachBtn.textContent = 'detach'; 2391 2391 detachBtn.className = 'btn btn-danger'; 2392 2392 detachBtn.onclick = () => detachEntity(entity); 2393 2393 } ··· 2400 2400 2401 2401 const descSpan = document.createElement('span'); 2402 2402 descSpan.className = entity.description ? 'entity-desc-display' : 'entity-desc-display empty'; 2403 - descSpan.textContent = entity.description || 'No description. Click to add...'; 2403 + descSpan.textContent = entity.description || 'no description. click to add...'; 2404 2404 descSpan.style.cursor = 'pointer'; 2405 2405 descSpan.onclick = () => startDescriptionEdit(entity); 2406 2406 ··· 2417 2417 const textarea = document.createElement('textarea'); 2418 2418 textarea.className = 'entity-desc-input'; 2419 2419 textarea.value = entity.description || ''; 2420 - textarea.placeholder = 'Enter description...'; 2420 + textarea.placeholder = 'enter description...'; 2421 2421 2422 2422 const btnContainer = document.createElement('div'); 2423 2423 btnContainer.style.display = 'flex'; ··· 2428 2428 generateBtn.type = 'button'; 2429 2429 generateBtn.className = 'btn-icon'; 2430 2430 generateBtn.textContent = '✨'; 2431 - generateBtn.title = 'Generate with AI'; 2431 + generateBtn.title = 'generate with AI'; 2432 2432 2433 2433 const saveBtn = document.createElement('button'); 2434 2434 saveBtn.type = 'button'; 2435 2435 saveBtn.className = 'btn btn-primary btn-sm'; 2436 - saveBtn.textContent = 'Save'; 2436 + saveBtn.textContent = 'save'; 2437 2437 2438 2438 const cancelBtn = document.createElement('button'); 2439 2439 cancelBtn.type = 'button'; 2440 2440 cancelBtn.className = 'btn btn-secondary btn-sm'; 2441 - cancelBtn.textContent = 'Cancel'; 2441 + cancelBtn.textContent = 'cancel'; 2442 2442 2443 2443 btnContainer.appendChild(generateBtn); 2444 2444 btnContainer.appendChild(saveBtn); ··· 2588 2588 const attachError = document.getElementById('attach-error'); 2589 2589 2590 2590 attachBtn.disabled = true; 2591 - attachBtn.textContent = 'Attaching...'; 2591 + attachBtn.textContent = 'attaching...'; 2592 2592 attachError.style.display = 'none'; 2593 2593 2594 2594 fetch(`api/${encodeURIComponent(currentFacet)}`, { ··· 2607 2607 showDetailView(entity.id); 2608 2608 loadEntities(); // Refresh list in background 2609 2609 } else { 2610 - throw new Error(data.error || 'Failed to attach'); 2610 + throw new Error(data.error || 'failed to attach'); 2611 2611 } 2612 2612 }) 2613 2613 .catch(error => { ··· 2615 2615 attachError.textContent = error.message; 2616 2616 attachError.style.display = 'block'; 2617 2617 attachBtn.disabled = false; 2618 - attachBtn.textContent = 'Attach to Facet'; 2618 + attachBtn.textContent = 'attach to facet'; 2619 2619 }); 2620 2620 } 2621 2621 ··· 2881 2881 2882 2882 const blockedBadge = document.createElement('div'); 2883 2883 blockedBadge.className = 'entity-card-blocked-badge'; 2884 - blockedBadge.textContent = 'Blocked'; 2884 + blockedBadge.textContent = 'blocked'; 2885 2885 card.appendChild(blockedBadge); 2886 2886 } 2887 2887 ··· 2898 2898 const badge = document.createElement('div'); 2899 2899 badge.className = 'entity-card-badge'; 2900 2900 badge.textContent = entity.total_observation_count; 2901 - badge.title = `${entity.total_observation_count} mention${entity.total_observation_count !== 1 ? 's' : ''} — observations recorded about this entity`; 2901 + badge.title = `${entity.total_observation_count} mention${entity.total_observation_count !== 1 ? 's' : ''} — observations about this entity`; 2902 2902 card.appendChild(badge); 2903 2903 } 2904 2904 ··· 3061 3061 const badge = document.createElement('div'); 3062 3062 badge.className = 'entity-card-mention-badge'; 3063 3063 badge.textContent = entity.observation_count; 3064 - badge.title = `${entity.observation_count} mention${entity.observation_count > 1 ? 's' : ''} — observations recorded about this entity`; 3064 + badge.title = `${entity.observation_count} mention${entity.observation_count > 1 ? 's' : ''} — observations about this entity`; 3065 3065 card.appendChild(badge); 3066 3066 } 3067 3067 ··· 3115 3115 3116 3116 const meta = document.createElement('div'); 3117 3117 meta.className = 'entity-card-meta'; 3118 - meta.textContent = 'Adding...'; 3118 + meta.textContent = 'adding...'; 3119 3119 card.appendChild(meta); 3120 3120 3121 3121 return card;
+11 -11
apps/graph/workspace.html
··· 15 15 <button class="graph-time-btn" data-days="">all</button> 16 16 </div> 17 17 <div class="graph-strength-filter"> 18 - <label for="min-strength" title="filter out weaker connections -- higher means only stronger relationships">Min strength</label> 18 + <label for="min-strength" title="filter out weaker connections -- higher means only stronger relationships">min strength</label> 19 19 <input type="range" id="min-strength" min="0" max="500" value="0" step="5"> 20 20 <span id="min-strength-val">0</span> 21 21 </div> ··· 35 35 <div class="graph-empty" id="graph-empty" style="display:none;"> 36 36 <div class="surface-state surface-state--empty"> 37 37 <div class="surface-state-icon" aria-hidden="true">🕸️</div> 38 - <h2 class="surface-state-heading">Your knowledge graph builds itself from daily use</h2> 38 + <h2 class="surface-state-heading">your knowledge graph builds itself from daily use</h2> 39 39 <p class="surface-state-desc">as solstone takes in your meetings, conversations, and work, entities and relationships appear here automatically.</p> 40 40 <div class="surface-state-action"><a href="/app/home" class="graph-empty-action">get started with solstone →</a></div> 41 41 </div> ··· 48 48 <div class="surface-state-action"><a href="#" id="graph-retry">try again</a></div> 49 49 </div> 50 50 </div> 51 - <div id="graph-canvas" tabindex="0" role="img" aria-label="Knowledge graph" style="width:100%;height:100%;display:none;"></div> 51 + <div id="graph-canvas" tabindex="0" role="img" aria-label="knowledge graph" style="width:100%;height:100%;display:none;"></div> 52 52 <div id="graph-stabilize"><div id="graph-stabilize-bar"></div></div> 53 53 <div id="graph-reload-overlay"><div class="graph-spinner"></div></div> 54 54 <div id="graph-sr-summary" class="sr-only" aria-live="polite"></div> ··· 56 56 <table class="graph-table"> 57 57 <thead> 58 58 <tr> 59 - <th role="button" tabindex="0" aria-sort="none" data-col="name">Name</th> 60 - <th role="button" tabindex="0" aria-sort="none" data-col="type">Type</th> 61 - <th role="button" tabindex="0" aria-sort="descending" data-col="score">Score</th> 62 - <th role="button" tabindex="0" aria-sort="none" data-col="connections">Connections</th> 59 + <th role="button" tabindex="0" aria-sort="none" data-col="name">name</th> 60 + <th role="button" tabindex="0" aria-sort="none" data-col="type">type</th> 61 + <th role="button" tabindex="0" aria-sort="descending" data-col="score">score</th> 62 + <th role="button" tabindex="0" aria-sort="none" data-col="connections">connections</th> 63 63 </tr> 64 64 </thead> 65 65 <tbody id="graph-list-body"></tbody> ··· 68 68 </div> 69 69 70 70 <!-- Entity detail panel (slide-in from right) --> 71 - <div class="graph-detail-panel" id="graph-detail-panel" tabindex="-1" aria-label="Entity details"> 71 + <div class="graph-detail-panel" id="graph-detail-panel" tabindex="-1" aria-label="entity details"> 72 72 <div class="graph-detail-header"> 73 73 <h2 id="detail-name" tabindex="-1"></h2> 74 74 <button class="graph-detail-close" id="detail-close">&times;</button> ··· 945 945 } 946 946 const top = data.nodes.slice().sort((a, b) => (b.score || 0) - (a.score || 0)).slice(0, 10); 947 947 const topList = top.map(n => n.name + ' (' + n.type + ')').join(', '); 948 - el.textContent = 'Knowledge graph showing ' + data.nodes.length + ' entities and ' + data.edges.length + ' connections. Top entities: ' + topList + '.'; 948 + el.textContent = 'knowledge graph showing ' + data.nodes.length + ' entities and ' + data.edges.length + ' connections. top entities: ' + topList + '.'; 949 949 } 950 950 951 951 function renderList() { ··· 1030 1030 html += '<div>'; 1031 1031 html += '<span class="detail-type-badge" style="background:' + typeColor + '">' + escapeHtml(identity.type || nodeData.type) + '</span>'; 1032 1032 if (identity.is_principal) { 1033 - html += '<span class="detail-principal-badge">You</span>'; 1033 + html += '<span class="detail-principal-badge">you</span>'; 1034 1034 } 1035 1035 html += '</div>'; 1036 1036 ··· 1084 1084 } 1085 1085 1086 1086 // Link to entities app 1087 - html += '<a class="detail-entity-link" href="/app/entities#' + encodeURIComponent(identity.entity_id || nodeData.id) + '">View full intelligence →</a>'; 1087 + html += '<a class="detail-entity-link" href="/app/entities#' + encodeURIComponent(identity.entity_id || nodeData.id) + '">view full intelligence →</a>'; 1088 1088 1089 1089 bodyEl.innerHTML = html; 1090 1090
+70 -70
apps/health/workspace.html
··· 754 754 } 755 755 756 756 .logs-viewport:empty::before { 757 - content: 'Waiting for log output...'; 757 + content: 'waiting for log output...'; 758 758 color: #6b7280; 759 759 font-style: italic; 760 760 } ··· 1077 1077 <span class="skeleton" style="width: 120px; height: 1em;">&nbsp;</span> 1078 1078 </div> 1079 1079 <span class="connection-indicator" id="connectionIndicator"></span> 1080 - <button id="vitalsCheckBtn">Check now</button> 1080 + <button id="vitalsCheckBtn">check now</button> 1081 1081 </div> 1082 1082 <div class="vitals-content"> 1083 1083 <div class="vitals-section"> ··· 1115 1115 <div class="vitals-value" id="schedulesValue"></div> 1116 1116 </div> 1117 1117 </div> 1118 - <div class="trust-indicator" id="trustIndicator">🔒 All data stored locally on this machine</div> 1118 + <div class="trust-indicator" id="trustIndicator">🔒 all data stored locally on this machine</div> 1119 1119 </div> 1120 1120 1121 1121 <div class="status-summary"> ··· 1129 1129 observations 1130 1130 </div> 1131 1131 <div class="health-badge idle" id="observeModeBadge"> 1132 - <span id="observeModeLabel">Waiting...</span> 1132 + <span id="observeModeLabel">waiting...</span> 1133 1133 </div> 1134 1134 </div> 1135 1135 <div id="observeEmpty"> 1136 1136 <div class="surface-state surface-state--empty"> 1137 1137 <h2 class="surface-state-heading">no observations active</h2> 1138 - <div class="surface-state-action"><a href="/app/observer/">Manage observers →</a></div> 1138 + <div class="surface-state-action"><a href="/app/observer/">manage observers →</a></div> 1139 1139 </div> 1140 1140 </div> 1141 1141 <div class="observe-content hidden" id="observeContent"> 1142 1142 <div class="observe-section"> 1143 - <div class="observe-section-title">Screen Recording</div> 1144 - <div class="observe-section-value" id="screencastStatus">Waiting...</div> 1143 + <div class="observe-section-title">screen observing</div> 1144 + <div class="observe-section-value" id="screencastStatus">waiting...</div> 1145 1145 <div class="observe-section-detail" id="screencastDetail"></div> 1146 1146 </div> 1147 1147 <div class="observe-section"> 1148 - <div class="observe-section-title">Terminal Sessions</div> 1149 - <div class="observe-section-value" id="tmuxStatus">Waiting...</div> 1148 + <div class="observe-section-title">terminal sessions</div> 1149 + <div class="observe-section-value" id="tmuxStatus">waiting...</div> 1150 1150 <div class="observe-section-detail" id="tmuxDetail"></div> 1151 1151 </div> 1152 1152 <div class="observe-section"> 1153 1153 <div class="observe-section-title">audio</div> 1154 - <div class="observe-section-value" id="audioStatus">Waiting...</div> 1154 + <div class="observe-section-value" id="audioStatus">waiting...</div> 1155 1155 <div class="observe-section-detail" id="audioDetail"></div> 1156 1156 </div> 1157 1157 <div class="observe-section"> 1158 1158 <div class="observe-section-title">System Activity</div> 1159 - <div class="observe-section-value" id="activityStatus">Waiting...</div> 1159 + <div class="observe-section-value" id="activityStatus">waiting...</div> 1160 1160 <div class="observe-section-detail" id="activityDetail"></div> 1161 1161 </div> 1162 1162 <div class="observe-section"> 1163 1163 <div class="observe-section-title">Scene Analysis</div> 1164 - <div class="observe-section-value" id="describeStatus">Idle</div> 1164 + <div class="observe-section-value" id="describeStatus">idle</div> 1165 1165 <div class="observe-section-detail" id="describeDetail"></div> 1166 1166 </div> 1167 1167 <div class="observe-section"> 1168 1168 <div class="observe-section-title">Audio Transcription</div> 1169 - <div class="observe-section-value" id="transcribeStatus">Idle</div> 1169 + <div class="observe-section-value" id="transcribeStatus">idle</div> 1170 1170 <div class="observe-section-detail" id="transcribeDetail"></div> 1171 1171 </div> 1172 1172 </div> ··· 1184 1184 1185 1185 <div class="dashboard-card registered-observers-card hidden" id="registeredObserversCard"> 1186 1186 <div class="card-header"> 1187 - <div class="card-title">Registered observers</div> 1187 + <div class="card-title">registered observers</div> 1188 1188 </div> 1189 1189 <div class="registered-observers-strip" id="registeredObserversStrip"></div> 1190 1190 </div> ··· 1217 1217 1218 1218 <div class="dashboard-card activity-section hidden" id="allQuietCard"> 1219 1219 <div class="idle-card"> 1220 - <div class="idle-card-title">Everything is idle</div> 1220 + <div class="idle-card-title">everything is idle</div> 1221 1221 <div class="idle-card-stats" id="idleCardStats"></div> 1222 1222 </div> 1223 1223 </div> ··· 1225 1225 <!-- Think Card (hidden when idle) --> 1226 1226 <div class="dashboard-card think-card hidden" id="thinkCard"> 1227 1227 <div class="card-header"> 1228 - <div class="card-title">Background analysis</div> 1228 + <div class="card-title">background analysis</div> 1229 1229 </div> 1230 1230 <div class="think-info" id="thinkInfo"></div> 1231 1231 <div id="thinkProgress"></div> ··· 1235 1235 <!-- Sync Card (hidden when idle) --> 1236 1236 <div class="dashboard-card sync-card hidden" id="syncCard"> 1237 1237 <div class="card-header"> 1238 - <div class="card-title">Cloud sync</div> 1238 + <div class="card-title">cloud sync</div> 1239 1239 </div> 1240 1240 <div class="sync-info" id="syncInfo"></div> 1241 1241 </div> ··· 1243 1243 <!-- Service Logs --> 1244 1244 <div class="dashboard-card logs-card logs-collapsed"> 1245 1245 <div class="logs-header" role="button" tabindex="0" aria-expanded="false" aria-controls="logsViewport"> 1246 - <div class="logs-title">Service logs</div> 1246 + <div class="logs-title">service logs</div> 1247 1247 <span class="logs-summary-badge" id="logsSummaryBadge" aria-live="polite"></span> 1248 1248 <span class="logs-error-badge hidden" id="logErrorBadge"></span> 1249 - <span class="logs-collapse-indicator" id="logsCollapseIndicator">▶ Show</span> 1249 + <span class="logs-collapse-indicator" id="logsCollapseIndicator">▶ show</span> 1250 1250 <div class="logs-controls"> 1251 1251 <select id="logServiceFilter"> 1252 - <option value="all">All Services</option> 1252 + <option value="all">all services</option> 1253 1253 </select> 1254 1254 <select id="logStreamFilter"> 1255 1255 <option value="all">All Streams</option> ··· 1257 1257 <option value="stderr">stderr</option> 1258 1258 <option value="log">log</option> 1259 1259 </select> 1260 - <button id="logFollowBtn" class="active">Follow</button> 1261 - <button id="logClearBtn">Clear</button> 1262 - <button id="logExportBtn">Export</button> 1260 + <button id="logFollowBtn" class="active">follow</button> 1261 + <button id="logClearBtn">clear</button> 1262 + <button id="logExportBtn">export</button> 1263 1263 </div> 1264 1264 </div> 1265 1265 <div class="logs-viewport" id="logsViewport" role="log"></div> ··· 1276 1276 elements.statusSummaryText.textContent = ''; 1277 1277 const errSpan = document.createElement('span'); 1278 1278 errSpan.style.color = '#dc2626'; 1279 - errSpan.textContent = "Couldn't connect to solstone — is it running?"; 1279 + errSpan.textContent = "couldn't connect to solstone — is it running?"; 1280 1280 elements.statusSummaryText.appendChild(errSpan); 1281 1281 1282 1282 elements.vitalsStatus.textContent = ''; ··· 1391 1391 observe: 'Screen & Audio', 1392 1392 think: 'Background Analysis', 1393 1393 sync: 'Cloud Sync', 1394 - importer: 'File Importer', 1394 + importer: 'file importer', 1395 1395 schedule: 'Task Scheduler', 1396 1396 }; 1397 1397 ··· 1486 1486 if (observers.some(observer => observer.screencast?.recording)) captureDetails.push('screen'); 1487 1487 if (observers.some(observer => observer.audio && ((observer.audio.threshold_hits || 0) > 0 || observer.audio.will_save))) captureDetails.push('audio'); 1488 1488 if (observers.some(observer => observer.tmux?.capturing)) captureDetails.push('terminal'); 1489 - parts.push(captureDetails.length > 0 ? `Capturing (${captureDetails.join(', ')})` : 'Capturing'); 1489 + parts.push(captureDetails.length > 0 ? `observing (${captureDetails.join(', ')})` : 'observing'); 1490 1490 } 1491 1491 1492 1492 const processingDetails = []; ··· 1514 1514 1515 1515 if (parts.length === 0) { 1516 1516 if (servicesKnown) { 1517 - elements.statusSummaryText.textContent = 'Healthy — all quiet'; 1517 + elements.statusSummaryText.textContent = 'healthy — all quiet'; 1518 1518 } else if (state.connected) { 1519 - elements.statusSummaryText.textContent = 'Waiting for data...'; 1519 + elements.statusSummaryText.textContent = 'waiting for data...'; 1520 1520 } 1521 1521 return; 1522 1522 } 1523 1523 1524 1524 if (servicesKnown && state.crashed.size === 0 && staleCount === 0 && state.recentErrors.length === 0) { 1525 - parts.push('Healthy'); 1525 + parts.push('healthy'); 1526 1526 } 1527 1527 1528 1528 elements.statusSummaryText.textContent = parts.join(' · '); ··· 1554 1554 if (state.lastAgentFinishTs) { 1555 1555 const ago = formatElapsed(Math.floor((Date.now() - state.lastAgentFinishTs) / 1000)); 1556 1556 const el = ensureChild(idx++); 1557 - el.textContent = 'Last agent finished ' + ago + ' ago'; 1557 + el.textContent = 'last agent finished ' + ago + ' ago'; 1558 1558 el.style.color = ''; 1559 1559 } 1560 1560 ··· 1564 1564 errEl.textContent = errCount + ' recent error' + (errCount !== 1 ? 's' : ''); 1565 1565 errEl.style.color = '#dc2626'; 1566 1566 } else { 1567 - errEl.textContent = 'No recent errors'; 1567 + errEl.textContent = 'no recent errors'; 1568 1568 errEl.style.color = ''; 1569 1569 } 1570 1570 ··· 1574 1574 .sort((a, b) => a.next_run - b.next_run)[0]; 1575 1575 if (nextSchedule) { 1576 1576 const el = ensureChild(idx++); 1577 - el.textContent = 'Next: ' + (nextSchedule.name || 'scheduled') + ' ' + formatNextRun(nextSchedule.next_run); 1577 + el.textContent = 'next: ' + (nextSchedule.name || 'scheduled') + ' ' + formatNextRun(nextSchedule.next_run); 1578 1578 el.style.color = ''; 1579 1579 } 1580 1580 ··· 1686 1686 btn.setAttribute('data-action', 'view-logs'); 1687 1687 btn.setAttribute('data-service', 'cortex'); 1688 1688 btn.className = 'error-advice-link'; 1689 - btn.textContent = 'View AI Engine logs'; 1689 + btn.textContent = 'view ai engine logs'; 1690 1690 advice.appendChild(btn); 1691 1691 advice.appendChild(document.createTextNode(' for details.')); 1692 1692 container.appendChild(advice); ··· 1696 1696 const stageText = importErr?.stage && importErr.stage !== 'unknown' ? ' at ' + importErr.stage : ''; 1697 1697 const advice = document.createElement('div'); 1698 1698 advice.style.cssText = 'padding: 0.25em 0; font-size: 0.8em; color: #9ca3af;'; 1699 - advice.appendChild(document.createTextNode('Import failed' + stageText + '. Check the source file and retry, or ')); 1699 + advice.appendChild(document.createTextNode('import failed' + stageText + '. check the source file and retry, or ')); 1700 1700 const btn = document.createElement('button'); 1701 1701 btn.setAttribute('data-action', 'view-logs'); 1702 1702 btn.setAttribute('data-service', 'importer'); 1703 1703 btn.className = 'error-advice-link'; 1704 - btn.textContent = 'view File Importer logs'; 1704 + btn.textContent = 'view file importer logs'; 1705 1705 advice.appendChild(btn); 1706 1706 advice.appendChild(document.createTextNode('.')); 1707 1707 container.appendChild(advice); ··· 1710 1710 if (Array.from(types).some(t => t !== 'agent' && t !== 'import')) { 1711 1711 const advice = document.createElement('div'); 1712 1712 advice.style.cssText = 'padding: 0.25em 0; font-size: 0.8em; color: #9ca3af;'; 1713 - advice.appendChild(document.createTextNode('Service error detected. ')); 1713 + advice.appendChild(document.createTextNode('service error detected. ')); 1714 1714 const btn = document.createElement('button'); 1715 1715 btn.setAttribute('data-action', 'view-logs'); 1716 1716 btn.setAttribute('data-service', 'supervisor'); 1717 1717 btn.className = 'error-advice-link'; 1718 - btn.textContent = 'View System Manager logs'; 1718 + btn.textContent = 'view system manager logs'; 1719 1719 advice.appendChild(btn); 1720 1720 advice.appendChild(document.createTextNode(' for details.')); 1721 1721 container.appendChild(advice); ··· 1775 1775 const card = btn.closest('[data-key]'); 1776 1776 const errorEl = card?.querySelector('.activity-card-error'); 1777 1777 btn.disabled = true; 1778 - btn.textContent = 'Retrying...'; 1778 + btn.textContent = 'retrying...'; 1779 1779 window.apiJson('/app/health/api/retry-import', { 1780 1780 method: 'POST', 1781 1781 headers: { 'Content-Type': 'application/json' }, 1782 1782 body: JSON.stringify({ import_id: importId }) 1783 1783 }) 1784 1784 .then(() => { 1785 - btn.textContent = 'Retry sent'; 1785 + btn.textContent = 'retry sent'; 1786 1786 btn.style.color = '#9ca3af'; 1787 1787 if (errorEl) { 1788 1788 errorEl.textContent = ''; ··· 1791 1791 .catch((err) => { 1792 1792 window.logError(err, { context: 'health: retry-import failed' }); 1793 1793 btn.disabled = false; 1794 - btn.textContent = 'Retry'; 1794 + btn.textContent = 'retry'; 1795 1795 btn.style.color = ''; 1796 1796 if (errorEl) { 1797 - errorEl.textContent = err.serverMessage || 'Retry failed'; 1797 + errorEl.textContent = err.serverMessage || 'retry failed'; 1798 1798 } 1799 1799 }); 1800 1800 }); ··· 1915 1915 staleListSpan.style.display = ''; 1916 1916 updateVitalsStatus('warning'); 1917 1917 } else if (hasCrashed) { 1918 - mainSpan.textContent = 'Services need attention'; 1918 + mainSpan.textContent = 'services need attention'; 1919 1919 mainSpan.style.color = '#f87171'; 1920 1920 staleListSpan.textContent = ''; 1921 1921 staleListSpan.style.display = 'none'; ··· 2023 2023 if (status === 'ok') { 2024 2024 indicator.className = 'status-indicator active'; 2025 2025 indicator.setAttribute('aria-label', 'System status: healthy'); 2026 - text.textContent = 'All Systems Go'; 2026 + text.textContent = 'all systems go'; 2027 2027 severity.textContent = 'healthy'; 2028 2028 } else if (status === 'warning') { 2029 2029 indicator.className = 'status-indicator restarting'; 2030 2030 indicator.setAttribute('aria-label', 'System status: warning'); 2031 - text.textContent = 'Some services slow to respond'; 2031 + text.textContent = 'some services slow to respond'; 2032 2032 severity.textContent = 'warning'; 2033 2033 el.classList.add('warning'); 2034 2034 } else if (status === 'error') { 2035 2035 indicator.className = 'status-indicator crashed'; 2036 2036 indicator.setAttribute('aria-label', 'System status: error'); 2037 - text.textContent = 'Services need attention'; 2037 + text.textContent = 'services need attention'; 2038 2038 severity.textContent = 'error'; 2039 2039 el.classList.add('error'); 2040 2040 } ··· 2057 2057 label.textContent = 'observing'; 2058 2058 } else if (mode === 'tmux') { 2059 2059 badge.className = 'health-badge tmux'; 2060 - label.textContent = 'Terminal Sessions'; 2060 + label.textContent = 'terminal sessions'; 2061 2061 } else if (mode === 'idle') { 2062 2062 badge.className = 'health-badge idle'; 2063 - label.textContent = 'Idle'; 2063 + label.textContent = 'idle'; 2064 2064 } else { 2065 2065 badge.className = 'health-badge idle'; 2066 - label.textContent = 'Waiting...'; 2066 + label.textContent = 'waiting...'; 2067 2067 } 2068 2068 } 2069 2069 ··· 2096 2096 const displayLabel = streamCount === 1 ? 'display' : 'displays'; 2097 2097 const mins = Math.max(1, Math.round(elapsed / 60)); 2098 2098 return { 2099 - status: `Recording (${streamCount} ${displayLabel}, ~${mins} min)`, 2099 + status: `observing (${streamCount} ${displayLabel}, ~${mins} min)`, 2100 2100 detail: streamCount > 0 2101 2101 ? streams.map(s => `${s.position || 'unknown'} ${s.connector || 'unknown'}`).join(', ') 2102 2102 : '', ··· 2138 2138 { 2139 2139 statusEl: elements.activityStatus, 2140 2140 detailEl: elements.activityDetail, 2141 - idleText: 'Idle', 2141 + idleText: 'idle', 2142 2142 extract: () => { 2143 2143 if (!primary.activity) return null; 2144 2144 const idleMs = primary.activity.idle_time_ms || 0; 2145 2145 if (primary.activity.power_save) return { status: 'Power saving' }; 2146 2146 if (primary.activity.screen_locked) return { status: 'Screen locked' }; 2147 2147 if (primary.activity.sink_muted) return { status: 'Audio muted' }; 2148 - return { status: `Idle: ${Math.floor(idleMs/1000)}s` }; 2148 + return { status: `idle: ${Math.floor(idleMs/1000)}s` }; 2149 2149 }, 2150 2150 }, 2151 2151 { 2152 2152 statusEl: elements.describeStatus, 2153 2153 detailEl: elements.describeDetail, 2154 - idleText: 'Idle', 2154 + idleText: 'idle', 2155 2155 extract: () => { 2156 2156 if (!primary.describe) return null; 2157 2157 return { processor: primary.describe }; ··· 2160 2160 { 2161 2161 statusEl: elements.transcribeStatus, 2162 2162 detailEl: elements.transcribeDetail, 2163 - idleText: 'Idle', 2163 + idleText: 'idle', 2164 2164 extract: () => { 2165 2165 if (!primary.transcribe) return null; 2166 2166 return { processor: primary.transcribe }; ··· 2172 2172 2173 2173 if (!state.localHost || !primary) { 2174 2174 channels.forEach(ch => { 2175 - ch.statusEl.textContent = 'Waiting...'; 2175 + ch.statusEl.textContent = 'waiting...'; 2176 2176 ch.statusEl.classList.remove('idle'); 2177 2177 ch.detailEl.textContent = ''; 2178 2178 }); ··· 2191 2191 const isRunning = !!p.running; 2192 2192 const queued = p.queued?.length || 0; 2193 2193 if (isRunning && queued > 0) { 2194 - ch.statusEl.textContent = `Running (+${queued} queued)`; 2194 + ch.statusEl.textContent = `running (+${queued} queued)`; 2195 2195 } else if (isRunning) { 2196 - ch.statusEl.textContent = 'Running'; 2196 + ch.statusEl.textContent = 'running'; 2197 2197 } else if (queued > 0) { 2198 - ch.statusEl.textContent = `Queued: ${queued}`; 2198 + ch.statusEl.textContent = `queued: ${queued}`; 2199 2199 } else { 2200 - ch.statusEl.textContent = 'Idle'; 2200 + ch.statusEl.textContent = 'idle'; 2201 2201 } 2202 2202 if (isRunning && p.running.file) { 2203 2203 ch.detailEl.textContent = truncate(p.running.file.split('/').pop(), 30); ··· 2403 2403 container.appendChild(card); 2404 2404 } 2405 2405 const stateLabel = agent.event === 'thinking' ? 'Thinking...' : 2406 - (agent.event === 'tool_start' || agent.event === 'tool_end') ? 'Working...' : 'Running...'; 2406 + (agent.event === 'tool_start' || agent.event === 'tool_end') ? 'working...' : 'running...'; 2407 2407 const elapsed = agent.elapsed_seconds ? formatElapsed(agent.elapsed_seconds) : '0s'; 2408 2408 card.children[0].textContent = '...' + getAgentId(agent.use_id); 2409 2409 card.children[1].textContent = agent.name || 'default'; ··· 2462 2462 const retryBtn = document.createElement('button'); 2463 2463 retryBtn.setAttribute('data-action', 'retry'); 2464 2464 retryBtn.className = 'import-retry-btn'; 2465 - retryBtn.textContent = 'Retry'; 2465 + retryBtn.textContent = 'retry'; 2466 2466 retryWrap.appendChild(retryBtn); 2467 2467 card.appendChild(retryWrap); 2468 2468 const progressWrap = document.createElement('div'); ··· 2582 2582 2583 2583 // Current agents 2584 2584 if (d.current_agents && d.current_agents.length > 0) { 2585 - elements.thinkAgents.textContent = 'Running: ' + d.current_agents.join(', '); 2585 + elements.thinkAgents.textContent = 'running: ' + d.current_agents.join(', '); 2586 2586 } else { 2587 2587 elements.thinkAgents.textContent = ''; 2588 2588 } ··· 2967 2967 const header = document.querySelector('.logs-header'); 2968 2968 card.classList.toggle('logs-collapsed', state.logsCollapsed); 2969 2969 header.setAttribute('aria-expanded', String(!state.logsCollapsed)); 2970 - elements.logsCollapseIndicator.textContent = state.logsCollapsed ? '▶ Show' : '▼ Hide'; 2970 + elements.logsCollapseIndicator.textContent = state.logsCollapsed ? '▶ show' : '▼ hide'; 2971 2971 if (!state.logsCollapsed) renderLogs(); 2972 2972 } 2973 2973 document.querySelector('.logs-header').addEventListener('click', (e) => { ··· 2996 2996 state.lastLogFilter = null; 2997 2997 elements.logErrorBadge.textContent = ''; 2998 2998 elements.logErrorBadge.classList.add('hidden'); 2999 - elements.logServiceFilter.innerHTML = '<option value="all">All Services</option>'; 2999 + elements.logServiceFilter.innerHTML = '<option value="all">all services</option>'; 3000 3000 updateLogsBadge(); 3001 3001 renderLogs(); 3002 3002 }); 3003 3003 elements.vitalsCheckBtn.addEventListener('click', () => { 3004 - elements.vitalsCheckBtn.textContent = 'Checking...'; 3004 + elements.vitalsCheckBtn.textContent = 'checking...'; 3005 3005 elements.vitalsCheckBtn.disabled = true; 3006 3006 fetch('/app/health/api/info') 3007 3007 .then(r => r.json()) 3008 3008 .then(info => { 3009 3009 state.localHost = info.hostname; 3010 3010 updateObserve(); 3011 - elements.vitalsCheckBtn.textContent = 'Check now'; 3011 + elements.vitalsCheckBtn.textContent = 'check now'; 3012 3012 elements.vitalsCheckBtn.disabled = false; 3013 3013 }) 3014 3014 .catch(() => { 3015 - elements.vitalsCheckBtn.textContent = 'Check now'; 3015 + elements.vitalsCheckBtn.textContent = 'check now'; 3016 3016 elements.vitalsCheckBtn.disabled = false; 3017 3017 }); 3018 3018 }); ··· 3048 3048 elements.logsSummaryBadge.style.display = 'none'; 3049 3049 3050 3050 // Replace header with log file context 3051 - logsTitle.textContent = 'LOG FILE'; 3052 - logsControls.innerHTML = '<button id="logBackBtn">← Back to Dashboard</button>'; 3053 - viewport.innerHTML = '<div class="logs-line log">Loading...</div>'; 3051 + logsTitle.textContent = 'log file'; 3052 + logsControls.innerHTML = '<button id="logBackBtn">← back to dashboard</button>'; 3053 + viewport.innerHTML = '<div class="logs-line log">loading...</div>'; 3054 3054 3055 3055 fetch('/app/health/api/log?path=' + encodeURIComponent(deepLinkLog)) 3056 3056 .then(r => r.json().then(data => ({ok: r.ok, data}))) ··· 3069 3069 escaped.replace(/\n/g, '</div><div class="logs-line">') + '</div>'; 3070 3070 }) 3071 3071 .catch(() => { 3072 - viewport.innerHTML = '<div class="logs-line stderr">Network error loading log file</div>'; 3072 + viewport.innerHTML = '<div class="logs-line stderr">network error loading log file</div>'; 3073 3073 }); 3074 3074 3075 3075 document.addEventListener('click', function(e) {
+4 -4
apps/home/workspace.html
··· 1095 1095 <div class="pulse-briefing-summary">{{ briefing_summary }}</div> 1096 1096 {% endif %} 1097 1097 {% if briefing_phase == 'pending' and not briefing_exists %} 1098 - <div class="pulse-briefing-placeholder">Your morning briefing is being prepared...</div> 1098 + <div class="pulse-briefing-placeholder">your morning briefing is being prepared...</div> 1099 1099 {% endif %} 1100 1100 {% if briefing_exists %} 1101 1101 <div class="pulse-briefing-body" id="pulse-briefing-body"> ··· 1140 1140 <div class="pulse-narrative-content" id="pulse-narrative-content"></div> 1141 1141 <a href="#" class="pulse-tell-more" data-conversation="Tell me more about what's happening today">tell me more →</a> 1142 1142 {% if narrative_updated_at %} 1143 - <div class="pulse-narrative-meta">Updated at {{ narrative_updated_at }}</div> 1143 + <div class="pulse-narrative-meta">updated at {{ narrative_updated_at }}</div> 1144 1144 {% endif %} 1145 1145 </div> 1146 1146 </div> 1147 1147 {% elif segment_count > 0 %} 1148 1148 <div class="pulse-narrative" id="pulse-narrative"> 1149 1149 <h2 class="pulse-section-header">{{ narrative_header or "today's flow" }}</h2> 1150 - <div class="pulse-narrative-empty">Analysis will be available after the next processing cycle.</div> 1150 + <div class="pulse-narrative-empty">analysis will be available after the next processing cycle.</div> 1151 1151 </div> 1152 1152 {% endif %} 1153 1153 {% if narrative_content is none and segment_count == 0 and not show_welcome %} ··· 1644 1644 const hdr = document.querySelector('.pulse-narrative .pulse-section-header'); 1645 1645 if (hdr && data.narrative_header) hdr.textContent = data.narrative_header; 1646 1646 const meta = document.querySelector('.pulse-narrative .pulse-narrative-meta'); 1647 - if (meta && data.narrative_updated_at) meta.textContent = 'Updated at ' + data.narrative_updated_at; 1647 + if (meta && data.narrative_updated_at) meta.textContent = 'updated at ' + data.narrative_updated_at; 1648 1648 var summaryEl = document.querySelector('.pulse-narrative .pulse-section-summary'); 1649 1649 if (summaryEl) { 1650 1650 var st = data.narrative_header || '';
+36 -36
apps/import/_detail.html
··· 123 123 </style> 124 124 125 125 <div class="import-detail-content"> 126 - <a href="/app/import/" class="back-link">← Back to Imports</a> 126 + <a href="/app/import/" class="back-link">← back to imports</a> 127 127 128 128 <div class="import-header"> 129 - <h1>Import: {{ timestamp }}</h1> 129 + <h1>import: {{ timestamp }}</h1> 130 130 <div class="import-meta" id="importMeta"> 131 - <span>Loading...</span> 131 + <span>loading...</span> 132 132 </div> 133 133 </div> 134 134 135 135 <div class="tabs" role="tablist"> 136 - <div class="tab active" data-target="overview" tabindex="0" role="tab">Overview</div> 137 - <div class="tab" data-target="content" tabindex="0" role="tab">Content</div> 138 - <div class="tab" data-target="import-json" tabindex="0" role="tab">Upload Metadata</div> 139 - <div class="tab" data-target="imported-json" tabindex="0" role="tab">Processing Results</div> 136 + <div class="tab active" data-target="overview" tabindex="0" role="tab">overview</div> 137 + <div class="tab" data-target="content" tabindex="0" role="tab">content</div> 138 + <div class="tab" data-target="import-json" tabindex="0" role="tab">upload metadata</div> 139 + <div class="tab" data-target="imported-json" tabindex="0" role="tab">processing results</div> 140 140 </div> 141 141 142 142 <div class="tab-content active" id="overview"> 143 143 <div class="info-grid" id="overviewContent"> 144 - <div class="info-label">Loading...</div> 144 + <div class="info-label">loading...</div> 145 145 </div> 146 146 147 147 <div class="links-section" id="linksSection" style="display:none;"> 148 - <h3>Quick Links</h3> 148 + <h3>quick links</h3> 149 149 <div id="quickLinks"></div> 150 150 </div> 151 151 152 152 <div class="files-list" id="filesList" style="display:none;"> 153 - <h4>Created Files</h4> 153 + <h4>created files</h4> 154 154 <ul id="filesListContent"></ul> 155 155 </div> 156 156 </div> 157 157 158 158 <div class="tab-content" id="content"> 159 - <div id="contentLoading" class="no-data">Select this tab to browse imported content</div> 159 + <div id="contentLoading" class="no-data">select this tab to browse imported content</div> 160 160 <div id="contentContainer" style="display:none;"> 161 161 <div class="import-content-summary" id="contentSummary"></div> 162 162 <div class="import-content-controls"> 163 - <input type="text" id="contentSearch" class="import-content-search" placeholder="Search items..." /> 163 + <input type="text" id="contentSearch" class="import-content-search" placeholder="search items..." /> 164 164 <select id="contentMonth" class="import-content-month"> 165 - <option value="">All months</option> 165 + <option value="">all months</option> 166 166 </select> 167 167 </div> 168 168 <div id="contentItems" class="import-content-items"></div> ··· 224 224 function loadContent(page = 1) { 225 225 const container = document.getElementById('contentContainer'); 226 226 const loading = document.getElementById('contentLoading'); 227 - loading.textContent = 'Loading content...'; 227 + loading.textContent = 'loading content...'; 228 228 loading.style.display = 'block'; 229 229 230 230 const params = new URLSearchParams({page, per_page: 50}); ··· 248 248 contentLoaded = true; 249 249 }) 250 250 .catch(err => { 251 - loading.textContent = 'Error loading content'; 251 + loading.textContent = 'error loading content'; 252 252 console.error('Content load error:', err); 253 253 }); 254 254 } ··· 316 316 ${item.meta?.location ? ` · ${escapeContentHtml(item.meta.location)}` : ''} 317 317 ${item.meta?.author ? `· ${escapeContentHtml(item.meta.author)}` : ''} 318 318 ${item.meta?.tags?.length ? `· ${item.meta.tags.map(t => escapeContentHtml(t)).join(', ')}` : ''} 319 - ${calLink ? ` · <a href="${calLink}" class="import-content-cal-link" onclick="event.stopPropagation();">View in activities →</a>` : ''} 319 + ${calLink ? ` · <a href="${calLink}" class="import-content-cal-link" onclick="event.stopPropagation();">view in activities →</a>` : ''} 320 320 </div> 321 321 ${item.preview ? `<div class="import-content-item-preview">${escapeContentHtml(stripMarkdown(item.preview))}</div>` : ''} 322 322 <div class="import-content-item-body"><div class="no-data">Loading...</div></div> ··· 372 372 return; 373 373 } 374 374 let html = ''; 375 - html += `<button ${data.page <= 1 ? 'disabled' : ''} onclick="loadContent(${data.page - 1})">Prev</button>`; 375 + html += `<button ${data.page <= 1 ? 'disabled' : ''} onclick="loadContent(${data.page - 1})">prev</button>`; 376 376 const start = Math.max(1, data.page - 2); 377 377 const end = Math.min(data.pages, data.page + 2); 378 378 for (let i = start; i <= end; i++) { 379 379 html += `<button class="${i === data.page ? 'active' : ''}" onclick="loadContent(${i})">${i}</button>`; 380 380 } 381 - html += `<button ${data.page >= data.pages ? 'disabled' : ''} onclick="loadContent(${data.page + 1})">Next</button>`; 381 + html += `<button ${data.page >= data.pages ? 'disabled' : ''} onclick="loadContent(${data.page + 1})">next</button>`; 382 382 el.innerHTML = html; 383 383 } 384 384 ··· 527 527 } else if (data.imported_json) { 528 528 overviewHtml += `<div class="info-label">Processing Status:</div><div class="info-value">Completed</div>`; 529 529 overviewHtml += `<div class="info-label">Target Day:</div><div class="info-value">${data.imported_json.target_day || '-'}</div>`; 530 - overviewHtml += `<div class="info-label">Files Created:</div><div class="info-value">${data.imported_json.total_files_created || 0}</div>`; 531 - overviewHtml += `<div class="info-label">Processing Time:</div><div class="info-value">${new Date(data.imported_json.processing_completed).toLocaleString()}</div>`; 530 + overviewHtml += `<div class="info-label">files created:</div><div class="info-value">${data.imported_json.total_files_created || 0}</div>`; 531 + overviewHtml += `<div class="info-label">processing time:</div><div class="info-value">${new Date(data.imported_json.processing_completed).toLocaleString()}</div>`; 532 532 533 533 if (data.imported_json.merge_summary) { 534 534 const mergeSummary = data.imported_json.merge_summary; 535 535 overviewHtml += ` 536 536 <div class="merge-summary-card"> 537 - <h3>Merge Summary</h3> 537 + <h3>merge summary</h3> 538 538 <div class="merge-summary-grid"> 539 539 <div class="merge-summary-row"> 540 - <div class="merge-summary-label">Segments</div> 540 + <div class="merge-summary-label">segments</div> 541 541 <div class="merge-summary-value">${mergeSummary.segments_copied || 0} copied · ${mergeSummary.segments_skipped || 0} skipped · ${mergeSummary.segments_errored || 0} errored</div> 542 542 </div> 543 543 <div class="merge-summary-row"> 544 - <div class="merge-summary-label">Entities</div> 544 + <div class="merge-summary-label">entities</div> 545 545 <div class="merge-summary-value">${mergeSummary.entities_created || 0} created · ${mergeSummary.entities_merged || 0} merged · ${mergeSummary.entities_staged || 0} staged</div> 546 546 </div> 547 547 <div class="merge-summary-row"> 548 - <div class="merge-summary-label">Facets</div> 548 + <div class="merge-summary-label">facets</div> 549 549 <div class="merge-summary-value">${mergeSummary.facets_created || 0} created · ${mergeSummary.facets_merged || 0} merged</div> 550 550 </div> 551 551 <div class="merge-summary-row"> 552 - <div class="merge-summary-label">Imports</div> 552 + <div class="merge-summary-label">imports</div> 553 553 <div class="merge-summary-value">${mergeSummary.imports_copied || 0} copied · ${mergeSummary.imports_skipped || 0} skipped</div> 554 554 </div> 555 555 </div> ··· 560 560 const principalCollision = data.imported_json.principal_collision; 561 561 overviewHtml += ` 562 562 <div class="merge-collision-callout"> 563 - <h3>Owner identity differs between journals</h3> 564 - <p>This journal belongs to ${escapeHtml(principalCollision.target_name || '')}, and the imported archive marks ${escapeHtml(principalCollision.source_name || '')} as the owner. solstone kept this journal&#39;s owner and brought the other person in as a regular entity.</p> 563 + <h3>owner identity differs between journals</h3> 564 + <p>this journal belongs to ${escapeHtml(principalCollision.target_name || '')}, and the imported archive marks ${escapeHtml(principalCollision.source_name || '')} as the owner. solstone kept this journal&#39;s owner and brought the other person in as a regular entity.</p> 565 565 </div> 566 566 `; 567 567 } ··· 571 571 const summaryErrors = data.summary_errors || []; 572 572 const artifactPaths = data.merge_artifact_paths || null; 573 573 if (stagedEntities.length || erroredSegments.length || summaryErrors.length || artifactPaths) { 574 - let highlightsHtml = '<div class="merge-highlights"><h3>Merge Highlights</h3>'; 574 + let highlightsHtml = '<div class="merge-highlights"><h3>merge highlights</h3>'; 575 575 if (stagedEntities.length) { 576 - highlightsHtml += '<div class="merge-highlights-section"><h4>Staged entities</h4><ul class="merge-highlights-list">'; 576 + highlightsHtml += '<div class="merge-highlights-section"><h4>staged entities</h4><ul class="merge-highlights-list">'; 577 577 stagedEntities.forEach(item => { 578 578 highlightsHtml += `<li>${escapeHtml(item.source_name || '')} → ${escapeHtml(item.target_name || '')}<br><code>${escapeHtml(item.staging_path || '')}</code></li>`; 579 579 }); 580 580 highlightsHtml += '</ul></div>'; 581 581 } 582 582 if (erroredSegments.length) { 583 - highlightsHtml += '<div class="merge-highlights-section"><h4>Errored segments</h4><ul class="merge-highlights-list">'; 583 + highlightsHtml += '<div class="merge-highlights-section"><h4>errored segments</h4><ul class="merge-highlights-list">'; 584 584 erroredSegments.forEach(item => { 585 585 highlightsHtml += `<li><strong>${escapeHtml(item.item_id || '')}</strong>: ${escapeHtml(item.reason || '')}</li>`; 586 586 }); 587 587 highlightsHtml += '</ul></div>'; 588 588 } 589 589 if (summaryErrors.length) { 590 - highlightsHtml += '<div class="merge-highlights-section"><h4>Summary errors</h4><ul class="merge-highlights-list">'; 590 + highlightsHtml += '<div class="merge-highlights-section"><h4>summary errors</h4><ul class="merge-highlights-list">'; 591 591 summaryErrors.forEach(item => { 592 592 highlightsHtml += `<li>${escapeHtml(item || '')}</li>`; 593 593 }); ··· 613 613 linksSection.style.display = 'block'; 614 614 615 615 let linksHtml = ''; 616 - linksHtml += `<div class="link-item">📅 <a href="/app/activities/${data.imported_json.target_day}">View Day in Activities</a></div>`; 617 - linksHtml += `<div class="link-item">💬 <a href="/app/transcripts/${data.imported_json.target_day}">View Transcript</a></div>`; 616 + linksHtml += `<div class="link-item">📅 <a href="/app/activities/${data.imported_json.target_day}">view day in activities</a></div>`; 617 + linksHtml += `<div class="link-item">💬 <a href="/app/transcripts/${data.imported_json.target_day}">view transcript</a></div>`; 618 618 quickLinks.innerHTML = linksHtml; 619 619 } 620 620 ··· 645 645 .catch(err => { 646 646 const serverMessage = err?.serverMessage || err?.message || ''; 647 647 document.getElementById('importMeta').innerHTML = `<div class="surface-state-refresh-error">Couldn&#39;t load import details.${serverMessage ? ` ${escapeHtml(serverMessage)}` : ''}</div>`; 648 - document.getElementById('overviewContent').innerHTML = '<div class="no-data">Overview unavailable</div>'; 649 - document.getElementById('importJsonContent').textContent = 'Upload metadata unavailable'; 650 - document.getElementById('importedJsonContent').textContent = 'Processing results unavailable'; 648 + document.getElementById('overviewContent').innerHTML = '<div class="no-data">overview unavailable</div>'; 649 + document.getElementById('importJsonContent').textContent = 'upload metadata unavailable'; 650 + document.getElementById('importedJsonContent').textContent = 'processing results unavailable'; 651 651 document.getElementById('linksSection').style.display = 'none'; 652 652 document.getElementById('filesList').style.display = 'none'; 653 653 });
+3 -3
apps/import/routes.py
··· 139 139 }, 140 140 { 141 141 "name": "recording", 142 - "display_name": "Meeting Recording", 142 + "display_name": "meeting audio", 143 143 "emoji": "🎙️", 144 144 "icon": "mic", 145 - "description": "Import audio recordings of meetings or conversations", 145 + "description": "import audio observations of meetings or conversations", 146 146 "input_type": "file", 147 - "upload_prompt": "Upload an audio file (.m4a, .mp3, .wav)", 147 + "upload_prompt": "upload an audio file (.m4a, .mp3, .wav)", 148 148 "has_guide": False, 149 149 "accept": ",".join(sorted(MEDIA_EXTENSIONS)), 150 150 },
+54 -54
apps/import/workspace.html
··· 615 615 <div id="importGrid" class="import-source-grid"></div> 616 616 617 617 <div id="importGuide" class="import-guided-flow" style="display:none"> 618 - <a href="#" class="import-back-link" onclick="showGrid(); return false;">← Back to sources</a> 618 + <a href="#" class="import-back-link" onclick="showGrid(); return false;">← back to sources</a> 619 619 <div id="guideSteps"></div> 620 620 </div> 621 621 622 622 <div class="import-list"> 623 - <h2>Import History</h2> 623 + <h2>import history</h2> 624 624 <div id="importListContent"> 625 625 <div class="surface-state surface-state--loading" role="status" aria-busy="true"> 626 626 <div class="surface-state-spinner" aria-hidden="true"></div> 627 - <span class="surface-state-text" data-role="loading-status">Loading imports...</span> 627 + <span class="surface-state-text" data-role="loading-status">loading imports...</span> 628 628 </div> 629 629 </div> 630 630 </div> ··· 632 632 633 633 <div id="detectModal" class="modal" role="dialog" aria-modal="true" aria-labelledby="detectModalTitle"> 634 634 <div class="modal-content"> 635 - <h3 id="detectModalTitle">Confirm import details</h3> 636 - <button class="close" type="button" aria-label="Close" onclick="closeDetectModal()">&times;</button> 635 + <h3 id="detectModalTitle">confirm import details</h3> 636 + <button class="close" type="button" aria-label="close" onclick="closeDetectModal()">&times;</button> 637 637 <div class="confirm-fields"> 638 638 <div class="confirm-field"> 639 - <label for="timestampInput">Timestamp</label> 639 + <label for="timestampInput">timestamp</label> 640 640 <input id="timestampInput" /> 641 641 </div> 642 642 <div class="confirm-field"> 643 - <label for="facetSelect">Facet</label> 643 + <label for="facetSelect">facet</label> 644 644 <select id="facetSelect"> 645 - <option value="">None</option> 645 + <option value="">none</option> 646 646 </select> 647 647 </div> 648 648 <div class="confirm-field"> 649 - <label for="settingInput">Setting</label> 650 - <input id="settingInput" placeholder="e.g. Jer lunch with Joe" /> 649 + <label for="settingInput">setting</label> 650 + <input id="settingInput" placeholder="e.g. jer lunch with joe" /> 651 651 </div> 652 652 </div> 653 653 <input type="hidden" id="savedPath" /> 654 654 <div class="modal-actions"> 655 - <button id="startBtn" class="import-primary-btn">Start Import</button> 655 + <button id="startBtn" class="import-primary-btn">start import</button> 656 656 </div> 657 657 </div> 658 658 </div> ··· 972 972 <div class="import-history-header"> 973 973 <div class="import-history-summary">${total} imports total, ${totalEntriesWritten} entries imported, ${totalEntitiesSeeded} entities found</div> 974 974 <div class="import-history-filters"> 975 - <label for="importSourceFilter">Source</label> 975 + <label for="importSourceFilter">source</label> 976 976 <select id="importSourceFilter"> 977 977 ${sourceOptions} 978 978 </select> ··· 1059 1059 <div class="no-imports"> 1060 1060 <div class="no-imports-icon">📥</div> 1061 1061 <div class="no-imports-heading">No imports yet</div> 1062 - <div class="no-imports-body">Import your data from any supported source above.</div> 1063 - <div class="no-imports-action"><a href="#" onclick="scrollToGrid(); return false;" style="color: #0f4c81; font-weight: 600;">Choose a source →</a></div> 1062 + <div class="no-imports-body">import your data from any supported source above.</div> 1063 + <div class="no-imports-action"><a href="#" onclick="scrollToGrid(); return false;" style="color: #0f4c81; font-weight: 600;">choose a source →</a></div> 1064 1064 </div> 1065 1065 `; 1066 1066 return; ··· 1069 1069 let html = buildHistoryHeader(data.total, data.total_entries_written || 0, data.total_entities_seeded || 0); 1070 1070 html += '<table class="import-table">'; 1071 1071 html += '<thead><tr>'; 1072 - html += '<th>Imported At</th>'; 1073 - html += '<th>File</th>'; 1074 - html += '<th>Source</th>'; 1075 - html += '<th>Size</th>'; 1076 - html += '<th>Imported To</th>'; 1077 - html += '<th>Status</th>'; 1078 - html += '<th>Files Created</th>'; 1079 - html += '<th>Stats</th>'; 1080 - html += '<th>Duration</th>'; 1072 + html += '<th>imported at</th>'; 1073 + html += '<th>file</th>'; 1074 + html += '<th>source</th>'; 1075 + html += '<th>size</th>'; 1076 + html += '<th>imported to</th>'; 1077 + html += '<th>status</th>'; 1078 + html += '<th>files created</th>'; 1079 + html += '<th>stats</th>'; 1080 + html += '<th>duration</th>'; 1081 1081 html += '</tr></thead><tbody>'; 1082 1082 1083 1083 imports.forEach(imp => { ··· 1245 1245 row.classList.add('import-row--stalled'); 1246 1246 const statusCell = row.querySelector('.status-cell'); 1247 1247 if (statusCell) { 1248 - statusCell.innerHTML = `<span class="import-status stalled">Stalled</span><div class="progress-detail import-stalled-meta">Stalled — no updates in 10 minutes. Import ID: ${escapeHtml(importId)}. Reload to retry.</div>`; 1248 + statusCell.innerHTML = `<span class="import-status stalled">stalled</span><div class="progress-detail import-stalled-meta">stalled — no updates in 10 minutes. import ID: ${escapeHtml(importId)}. reload to retry.</div>`; 1249 1249 } 1250 1250 } 1251 1251 ··· 1299 1299 } 1300 1300 } else if (eventData.event === 'completed') { 1301 1301 if (statusCell) { 1302 - statusCell.innerHTML = '<span class="import-status success">Completed</span>'; 1302 + statusCell.innerHTML = '<span class="import-status success">completed</span>'; 1303 1303 } 1304 1304 if (filesCell && eventData.total_files_created !== undefined) { 1305 1305 filesCell.textContent = eventData.total_files_created; ··· 1313 1313 setTimeout(() => loadImports(), 1000); 1314 1314 } else if (eventData.event === 'error') { 1315 1315 if (statusCell) { 1316 - statusCell.innerHTML = `<span class="import-status failed">Failed</span><div class="progress-detail">${escapeHtml(eventData.error || 'Unknown error')}</div>`; 1316 + statusCell.innerHTML = `<span class="import-status failed">failed</span><div class="progress-detail">${escapeHtml(eventData.error || 'unknown error')}</div>`; 1317 1317 } 1318 1318 } 1319 1319 ··· 1347 1347 } 1348 1348 1349 1349 if (!source) { 1350 - guideSteps.innerHTML = '<div class="no-imports">Unknown import source</div>'; 1350 + guideSteps.innerHTML = '<div class="no-imports">unknown import source</div>'; 1351 1351 return; 1352 1352 } 1353 1353 ··· 1380 1380 placeholder="${escapeHtml(source.upload_prompt || 'Paste the full path to your folder')}" /> 1381 1381 <div id="guidedPathStatus" style="margin-top: 0.5em; font-size: 0.9em; color: #666;"></div> 1382 1382 ` : ` 1383 - <h4>Upload your export</h4> 1384 - <div class="import-upload-area" id="guidedDropArea" role="button" tabindex="0" aria-label="Choose file to upload"> 1383 + <h4>upload your export</h4> 1384 + <div class="import-upload-area" id="guidedDropArea" role="button" tabindex="0" aria-label="choose file to upload"> 1385 1385 <p>${escapeHtml(source.upload_prompt || 'Select a file to import')}</p> 1386 1386 <div class="import-upload-help">${escapeHtml(source.accept ? `Accepted: ${source.accept}` : 'Any supported file')}</div> 1387 1387 <input id="guidedFileInput" type="file" accept="${escapeHtml(source.accept || '')}" style="display:none" /> ··· 1392 1392 </div></li> 1393 1393 <li><div class="import-step"> 1394 1394 <div class="import-step-number">Step 3</div> 1395 - <h4>Configure and start</h4> 1395 + <h4>configure and start</h4> 1396 1396 <div class="import-inline-fields"> 1397 1397 <div class="confirm-field"> 1398 - <label for="guidedFacetSelect">Facet</label> 1398 + <label for="guidedFacetSelect">facet</label> 1399 1399 <select id="guidedFacetSelect"><option value="">None</option></select> 1400 1400 </div> 1401 1401 <div class="confirm-field"> 1402 - <label for="guidedSettingInput">Setting</label> 1403 - <input id="guidedSettingInput" placeholder="e.g. Weekly planning" /> 1402 + <label for="guidedSettingInput">setting</label> 1403 + <input id="guidedSettingInput" placeholder="e.g. weekly planning" /> 1404 1404 </div> 1405 1405 </div> 1406 1406 <div class="import-action-row"> 1407 - <button id="guidedStartBtn" class="import-primary-btn" type="submit">Start Import</button> 1407 + <button id="guidedStartBtn" class="import-primary-btn" type="submit">start import</button> 1408 1408 </div> 1409 1409 </div></li> 1410 1410 </ol></form> ··· 1458 1458 1459 1459 guideSteps.innerHTML = ` 1460 1460 <div> 1461 - <h3 class="import-guide-title">⚡ Quick Import</h3> 1461 + <h3 class="import-guide-title">⚡ quick import</h3> 1462 1462 <p class="import-guide-subtitle">Paste text or drop a file, then confirm the detected timestamp in the modal.</p> 1463 1463 </div> 1464 1464 <form id="importForm"> 1465 1465 <ol class="import-steps-list"> 1466 1466 <li><div class="import-step"> 1467 1467 <div class="import-step-number">Step 1</div> 1468 - <div class="import-upload-area" id="dropArea" role="button" tabindex="0" aria-label="Choose file to upload"> 1468 + <div class="import-upload-area" id="dropArea" role="button" tabindex="0" aria-label="choose file to upload"> 1469 1469 <p>Drag file here or click to select</p> 1470 1470 <input id="fileInput" type="file" style="display:none" /> 1471 1471 <div id="fileLabel" class="import-file-label"></div> ··· 1473 1473 </div></li> 1474 1474 <li><div class="import-step"> 1475 1475 <div class="import-step-number">Step 2</div> 1476 - <textarea id="pasteText" class="import-paste-text" placeholder="Paste text here..."></textarea> 1476 + <textarea id="pasteText" class="import-paste-text" placeholder="paste text here..."></textarea> 1477 1477 <div class="import-inline-fields"> 1478 1478 <div class="confirm-field"> 1479 - <label for="quickFacetSelect">Facet</label> 1479 + <label for="quickFacetSelect">facet</label> 1480 1480 <select id="quickFacetSelect"><option value="">None</option></select> 1481 1481 </div> 1482 1482 <div class="confirm-field"> 1483 - <label for="quickSettingInput">Setting</label> 1484 - <input id="quickSettingInput" placeholder="e.g. Paste from notes app" /> 1483 + <label for="quickSettingInput">setting</label> 1484 + <input id="quickSettingInput" placeholder="e.g. paste from notes app" /> 1485 1485 </div> 1486 1486 </div> 1487 1487 <div class="import-action-row"> 1488 - <button type="submit" id="validateBtn" class="import-primary-btn">Validate</button> 1488 + <button type="submit" id="validateBtn" class="import-primary-btn">validate</button> 1489 1489 </div> 1490 1490 </div></li> 1491 1491 </ol> ··· 1598 1598 } 1599 1599 1600 1600 validateBtn.disabled = true; 1601 - validateBtn.innerHTML = 'Validating<span class="spinner"></span>'; 1601 + validateBtn.innerHTML = 'validating<span class="spinner"></span>'; 1602 1602 1603 1603 try { 1604 1604 const fd = new FormData(); ··· 1620 1620 const response = await fetch('/app/import/api/save', { method: 'POST', body: fd }); 1621 1621 const data = await response.json(); 1622 1622 validateBtn.disabled = false; 1623 - validateBtn.textContent = 'Validate'; 1623 + validateBtn.textContent = 'validate'; 1624 1624 1625 1625 if (!response.ok) { 1626 1626 if (window.showError) { ··· 1631 1631 showDetect(data); 1632 1632 } catch (_err) { 1633 1633 validateBtn.disabled = false; 1634 - validateBtn.textContent = 'Validate'; 1634 + validateBtn.textContent = 'validate'; 1635 1635 if (window.showError) { 1636 1636 showError('Validation failed'); 1637 1637 } ··· 1679 1679 if (!container) return; 1680 1680 container.innerHTML = ` 1681 1681 <div class="import-dedup-notice"> 1682 - <span class="dedup-text">This file was already imported on ${escapeHtml(dedup.imported_at)}${dedup.entry_count ? ` (${dedup.entry_count} entries)` : ''}.</span> 1683 - <button type="button" id="dedupForceBtn">Re-import anyway</button> 1682 + <span class="dedup-text">this file was already imported on ${escapeHtml(dedup.imported_at)}${dedup.entry_count ? ` (${dedup.entry_count} entries)` : ''}.</span> 1683 + <button type="button" id="dedupForceBtn">re-import anyway</button> 1684 1684 </div> 1685 1685 `; 1686 1686 document.getElementById('dedupForceBtn').addEventListener('click', () => { 1687 1687 window._guidedForceImport = true; 1688 - container.innerHTML = '<div class="import-dedup-notice"><span class="dedup-text">Will re-import. Click Start Import to proceed.</span></div>'; 1688 + container.innerHTML = '<div class="import-dedup-notice"><span class="dedup-text">will re-import. click start import to proceed.</span></div>'; 1689 1689 }); 1690 1690 } 1691 1691 ··· 1721 1721 const originalText = startButton.textContent; 1722 1722 1723 1723 startButton.disabled = true; 1724 - startButton.innerHTML = 'Starting<span class="spinner"></span>'; 1724 + startButton.innerHTML = 'starting<span class="spinner"></span>'; 1725 1725 1726 1726 try { 1727 1727 let saved; ··· 1811 1811 <div><strong>Date range:</strong> ${escapeHtml(formatDateRange(range) || '-')}</div> 1812 1812 <div><strong>Duration:</strong> ${formatElapsed(completedInfo.duration_ms || 0)}</div> 1813 1813 <div class="import-action-row"> 1814 - <a class="import-secondary-btn" href="/app/import/${completedInfo.processed_timestamp || importId}#content">Browse what was imported →</a> 1815 - ${day ? `<a class="import-secondary-btn" href="/app/activities/${day}">View in activities</a>` : ''} 1816 - <a href="#" class="import-secondary-btn" onclick="showGrid(); return false;">Import another source</a> 1814 + <a class="import-secondary-btn" href="/app/import/${completedInfo.processed_timestamp || importId}#content">browse what was imported →</a> 1815 + ${day ? `<a class="import-secondary-btn" href="/app/activities/${day}">view in activities</a>` : ''} 1816 + <a href="#" class="import-secondary-btn" onclick="showGrid(); return false;">import another source</a> 1817 1817 </div> 1818 1818 </div> 1819 1819 `; ··· 1823 1823 <div class="import-completion-summary failed"> 1824 1824 <div><strong>Error:</strong> ${escapeHtml(errorText)}</div> 1825 1825 <div class="import-action-row"> 1826 - <a href="#" class="import-secondary-btn" onclick="showGrid(); return false;">Import another source</a> 1826 + <a href="#" class="import-secondary-btn" onclick="showGrid(); return false;">import another source</a> 1827 1827 </div> 1828 1828 </div> 1829 1829 `; ··· 1833 1833 <div><strong>Import ID:</strong> ${escapeHtml(importId)}</div> 1834 1834 <div>Stalled — no updates in 10 minutes. Reload to retry.</div> 1835 1835 <div class="import-action-row"> 1836 - <a href="#" class="import-secondary-btn" onclick="showGrid(); return false;">Import another source</a> 1836 + <a href="#" class="import-secondary-btn" onclick="showGrid(); return false;">import another source</a> 1837 1837 </div> 1838 1838 </div> 1839 1839 `; ··· 1864 1864 1865 1865 const originalText = startBtn.textContent; 1866 1866 startBtn.disabled = true; 1867 - startBtn.innerHTML = 'Starting<span class="spinner"></span>'; 1867 + startBtn.innerHTML = 'starting<span class="spinner"></span>'; 1868 1868 1869 1869 try { 1870 1870 const response = await fetch('/app/import/api/facet', {
+13 -13
apps/link/workspace.html
··· 1 1 <main class="link-dashboard"> 2 2 <header class="link-header"> 3 - <h1 class="link-h1">Reach your solstone from anywhere</h1> 3 + <h1 class="link-h1">reach your solstone from anywhere</h1> 4 4 <p class="link-trust">spl is blind by construction. Cloudflare and sol pbc cannot see anything inside the tunnel.</p> 5 5 <div class="link-status-row" id="link-status-panel"> 6 6 <span id="link-status-indicator" class="link-status-indicator" aria-live="polite"> 7 7 Service status: <span id="link-status-text">loading…</span> 8 8 </span> 9 - <button type="button" class="link-pair-btn" id="link-pair-btn">Pair a phone</button> 9 + <button type="button" class="link-pair-btn" id="link-pair-btn">pair a phone</button> 10 10 </div> 11 11 </header> 12 12 ··· 18 18 </div> 19 19 20 20 <section class="link-paired-section" aria-labelledby="link-paired-h2"> 21 - <h2 id="link-paired-h2">Paired devices</h2> 21 + <h2 id="link-paired-h2">paired devices</h2> 22 22 <div id="link-devices-list" class="link-devices-list" aria-live="polite"> 23 23 <p class="link-empty-state" id="link-empty-state">No phones paired yet.</p> 24 24 </div> ··· 26 26 27 27 <div id="link-pair-modal" class="link-modal" hidden role="dialog" aria-modal="true" aria-labelledby="link-pair-modal-title"> 28 28 <div class="link-modal-box"> 29 - <h3 id="link-pair-modal-title">Pair a phone</h3> 29 + <h3 id="link-pair-modal-title">pair a phone</h3> 30 30 <label class="link-modal-label"> 31 31 Device label 32 - <input type="text" id="link-device-label" placeholder="e.g. My iPhone" maxlength="80" /> 32 + <input type="text" id="link-device-label" placeholder="e.g. my iPhone" maxlength="80" /> 33 33 </label> 34 34 <div class="link-modal-actions"> 35 - <button type="button" id="link-pair-generate">Generate pair code</button> 36 - <button type="button" id="link-pair-cancel" class="link-modal-cancel">Cancel</button> 35 + <button type="button" id="link-pair-generate">generate pair code</button> 36 + <button type="button" id="link-pair-cancel" class="link-modal-cancel">cancel</button> 37 37 </div> 38 38 39 39 <div id="link-pair-code" class="link-pair-code" hidden> ··· 53 53 54 54 <div id="link-unpair-modal" class="link-modal" hidden role="dialog" aria-modal="true" aria-labelledby="link-unpair-title"> 55 55 <div class="link-modal-box"> 56 - <h3 id="link-unpair-title">Unpair this phone?</h3> 56 + <h3 id="link-unpair-title">unpair this phone?</h3> 57 57 <p>Are you sure? This phone will lose access immediately.</p> 58 58 <div class="link-modal-actions"> 59 - <button type="button" id="link-unpair-confirm" class="link-modal-danger">Unpair</button> 60 - <button type="button" id="link-unpair-cancel" class="link-modal-cancel">Cancel</button> 59 + <button type="button" id="link-unpair-confirm" class="link-modal-danger">unpair</button> 60 + <button type="button" id="link-unpair-cancel" class="link-modal-cancel">cancel</button> 61 61 </div> 62 62 </div> 63 63 </div> ··· 265 265 const btn = document.createElement('button'); 266 266 btn.className = 'link-unpair-btn'; 267 267 btn.type = 'button'; 268 - btn.textContent = 'Unpair'; 268 + btn.textContent = 'unpair'; 269 269 btn.addEventListener('click', () => openUnpair(d)); 270 270 row.appendChild(left); 271 271 row.appendChild(btn); ··· 281 281 const note = document.createElement('p'); 282 282 note.style.fontSize = '0.85em'; 283 283 note.style.color = '#555'; 284 - note.textContent = 'Paste this URL into solstone mobile (QR rendering lib not bundled yet):'; 284 + note.textContent = 'paste this URL into solstone mobile (QR rendering lib not bundled yet):'; 285 285 qrContainer.appendChild(note); 286 286 const code = document.createElement('code'); 287 287 code.textContent = data.pair_url; ··· 319 319 await refreshDevices(); 320 320 if (Date.now() - started > 310000) { 321 321 clearInterval(interval); 322 - pairWaiting.textContent = 'Timed out. Pair code expired.'; 322 + pairWaiting.textContent = 'timed out. pair code expired.'; 323 323 } 324 324 }, 2000); 325 325 } catch (e) {
+27 -27
apps/observer/workspace.html
··· 499 499 </div> 500 500 </div> 501 501 <section aria-label="observers"> 502 - <h2 class="section-title">Observers</h2> 502 + <h2 class="section-title">observers</h2> 503 503 <section aria-label="add observer"> 504 - <h2 class="section-title">Add Observer</h2> 504 + <h2 class="section-title">add observer</h2> 505 505 <form class="add-observer-form" id="addObserverForm"> 506 - <label for="observerName">Observer name</label> 506 + <label for="observerName">observer name</label> 507 507 <input type="text" id="observerName" placeholder="e.g., laptop, desktop" maxlength="64" required> 508 - <button type="submit">Add Observer</button> 508 + <button type="submit">add observer</button> 509 509 </form> 510 510 </section> 511 511 <div id="observersList" role="list"> 512 512 <div class="surface-state surface-state--loading" role="status" aria-busy="true"> 513 513 <div class="surface-state-spinner" aria-hidden="true"></div> 514 - <span class="surface-state-text" data-role="loading-status">Loading observers...</span> 514 + <span class="surface-state-text" data-role="loading-status">loading observers...</span> 515 515 </div> 516 516 </div> 517 517 </section> ··· 520 520 <!-- Observer Key Modal (for new observers and viewing existing keys) --> 521 521 <div id="keyModal" class="modal" role="dialog" aria-modal="true" aria-label="observer key"> 522 522 <div class="modal-content"> 523 - <button type="button" class="modal-close" id="keyModalClose" aria-label="Close">&times;</button> 524 - <h3 id="keyModalTitle">Observer: <span id="modalObserverName"></span></h3> 523 + <button type="button" class="modal-close" id="keyModalClose" aria-label="close">&times;</button> 524 + <h3 id="keyModalTitle">observer: <span id="modalObserverName"></span></h3> 525 525 <p>Use these credentials in your solstone app's service settings. Bearer authentication (header) is recommended; URL path auth is legacy.</p> 526 526 <div class="credential-label">Server URL</div> 527 527 <div class="command-box"> 528 528 <code id="serverUrlText"></code> 529 - <button class="copy-btn" id="copyServerUrlBtn">Copy</button> 529 + <button class="copy-btn" id="copyServerUrlBtn">copy</button> 530 530 </div> 531 531 <div class="credential-label">Key</div> 532 532 <div class="command-box"> 533 533 <code id="keyText"></code> 534 - <button class="copy-btn" id="revealKeyBtn">Reveal</button> 535 - <button class="copy-btn" id="copyKeyBtn">Copy</button> 534 + <button class="copy-btn" id="revealKeyBtn">reveal</button> 535 + <button class="copy-btn" id="copyKeyBtn">copy</button> 536 536 </div> 537 537 <p class="modal-warning"> 538 538 Keep this key secret — anyone with it can upload to your journal. 539 539 </p> 540 540 <div class="modal-actions"> 541 - <button id="doneBtn" class="modal-primary-btn">Done</button> 541 + <button id="doneBtn" class="modal-primary-btn">done</button> 542 542 </div> 543 543 </div> 544 544 </div> ··· 578 578 }; 579 579 580 580 function emptyStateHTML() { 581 - return '<h3 class="observer-empty-heading">Add your first observer</h3>'; 581 + return '<h3 class="observer-empty-heading">add your first observer</h3>'; 582 582 } 583 583 584 584 function statsHTML(observer, statusClass) { ··· 680 680 </div> 681 681 <div class="observer-stats">${statsHTML(observer, statusClass)}</div> 682 682 <div class="observer-actions"> 683 - ${observer.revoked ? '' : `<button onclick="viewObserverKey('${observer.key_prefix}', '${escapeHtml(observer.name)}')">View Key</button>`} 684 - ${observer.revoked ? '' : `<button class="danger" onclick="revokeObserver('${observer.key_prefix}', '${escapeHtml(observer.name)}')">Revoke</button>`} 683 + ${observer.revoked ? '' : `<button onclick="viewObserverKey('${observer.key_prefix}', '${escapeHtml(observer.name)}')">view key</button>`} 684 + ${observer.revoked ? '' : `<button class="danger" onclick="revokeObserver('${observer.key_prefix}', '${escapeHtml(observer.name)}')">revoke</button>`} 685 685 </div> 686 686 </div> 687 687 `; ··· 846 846 serverUrlText.textContent = window.location.origin; 847 847 currentFullKey = key; 848 848 keyText.textContent = key.slice(0, 8) + '••••••••••••••••'; 849 - revealKeyBtn.textContent = 'Reveal'; 849 + revealKeyBtn.textContent = 'reveal'; 850 850 if (revealTimer) { clearTimeout(revealTimer); revealTimer = null; } 851 851 keyModal.style.display = 'block'; 852 852 keyModalTrigger = trigger || null; ··· 911 911 function closeKeyModal() { 912 912 if (revealTimer) { clearTimeout(revealTimer); revealTimer = null; } 913 913 currentFullKey = null; 914 - revealKeyBtn.textContent = 'Reveal'; 914 + revealKeyBtn.textContent = 'reveal'; 915 915 keyModal.style.display = 'none'; 916 916 document.removeEventListener('keydown', handleKeyModalKeys); 917 917 if (keyModalTrigger && document.contains(keyModalTrigger)) { ··· 949 949 function setupCopyBtn(btn, codeEl) { 950 950 btn.onclick = () => { 951 951 copyText(codeEl.textContent).then(() => { 952 - btn.textContent = 'Copied!'; 952 + btn.textContent = 'copied!'; 953 953 btn.classList.add('copied'); 954 954 setTimeout(() => { 955 - btn.textContent = 'Copy'; 955 + btn.textContent = 'copy'; 956 956 btn.classList.remove('copied'); 957 957 }, 2000); 958 958 }, () => { 959 - btn.textContent = 'Failed'; 960 - setTimeout(() => { btn.textContent = 'Copy'; }, 2000); 959 + btn.textContent = 'failed'; 960 + setTimeout(() => { btn.textContent = 'copy'; }, 2000); 961 961 }); 962 962 }; 963 963 } ··· 966 966 copyKeyBtn.onclick = () => { 967 967 if (!currentFullKey) return; 968 968 copyText(currentFullKey).then(() => { 969 - copyKeyBtn.textContent = 'Copied!'; 969 + copyKeyBtn.textContent = 'copied!'; 970 970 copyKeyBtn.classList.add('copied'); 971 971 setTimeout(() => { 972 - copyKeyBtn.textContent = 'Copy'; 972 + copyKeyBtn.textContent = 'copy'; 973 973 copyKeyBtn.classList.remove('copied'); 974 974 }, 2000); 975 975 }, () => { 976 - copyKeyBtn.textContent = 'Failed'; 977 - setTimeout(() => { copyKeyBtn.textContent = 'Copy'; }, 2000); 976 + copyKeyBtn.textContent = 'failed'; 977 + setTimeout(() => { copyKeyBtn.textContent = 'copy'; }, 2000); 978 978 }); 979 979 }; 980 980 revealKeyBtn.onclick = () => { ··· 982 982 const isRevealed = keyText.textContent === currentFullKey; 983 983 if (isRevealed) { 984 984 keyText.textContent = currentFullKey.slice(0, 8) + '••••••••••••••••'; 985 - revealKeyBtn.textContent = 'Reveal'; 985 + revealKeyBtn.textContent = 'reveal'; 986 986 if (revealTimer) { clearTimeout(revealTimer); revealTimer = null; } 987 987 } else { 988 988 keyText.textContent = currentFullKey; 989 - revealKeyBtn.textContent = 'Hide'; 989 + revealKeyBtn.textContent = 'hide'; 990 990 if (revealTimer) clearTimeout(revealTimer); 991 991 revealTimer = setTimeout(() => { 992 992 if (currentFullKey && keyText.textContent === currentFullKey) { 993 993 keyText.textContent = currentFullKey.slice(0, 8) + '••••••••••••••••'; 994 - revealKeyBtn.textContent = 'Reveal'; 994 + revealKeyBtn.textContent = 'reveal'; 995 995 } 996 996 revealTimer = null; 997 997 }, 30000);
+5 -5
apps/reflections/workspace.html
··· 158 158 <header class="reflection-header"> 159 159 <div> 160 160 <p class="reflection-kicker">weekly reflection</p> 161 - <h2 class="reflection-title">Reflections</h2> 161 + <h2 class="reflection-title">reflections</h2> 162 162 <p class="reflection-subtitle">Available weekly reflections, newest first.</p> 163 163 </div> 164 164 </header> ··· 186 186 <p class="reflection-subtitle">week of {{ reflection_week_label }}</p> 187 187 </div> 188 188 <div class="reflection-actions"> 189 - <button type="button" class="reflection-button" id="copyReflectionButton">Copy</button> 190 - <a class="reflection-link-button" href="{{ pdf_url }}">Download PDF</a> 189 + <button type="button" class="reflection-button" id="copyReflectionButton">copy</button> 190 + <a class="reflection-link-button" href="{{ pdf_url }}">download PDF</a> 191 191 </div> 192 192 </header> 193 193 ··· 230 230 const response = await fetch(rawUrl, { credentials: 'same-origin' }); 231 231 if (!response.ok) throw new Error('copy failed'); 232 232 await copyText(await response.text()); 233 - copyButton.textContent = 'Copied'; 233 + copyButton.textContent = 'copied'; 234 234 } catch (_error) { 235 - copyButton.textContent = 'Failed'; 235 + copyButton.textContent = 'failed'; 236 236 } finally { 237 237 window.setTimeout(() => { 238 238 copyButton.textContent = originalLabel;
+7 -7
apps/search/workspace.html
··· 454 454 <div class="filter-list" id="facet-list"> 455 455 <label class="filter-item active"> 456 456 <input type="radio" name="facet" value="" checked> 457 - <span class="filter-label">All</span> 457 + <span class="filter-label">all</span> 458 458 </label> 459 459 </div> 460 460 </div> ··· 472 472 <form class="search-input-form" id="search-input-form" role="search"> 473 473 <label for="search-input" class="visually-hidden">search your journal</label> 474 474 <input type="search" class="search-input" id="search-input" 475 - placeholder="Search your journal..." autocomplete="off"> 475 + placeholder="search your journal..." autocomplete="off"> 476 476 </form> 477 477 478 478 <div class="search-summary" id="search-summary" role="status" aria-live="polite" style="display: none;"></div> ··· 486 486 <div class="surface-state-action"> 487 487 <div class="search-example-chips"> 488 488 <button class="search-example-chip" type="button">a recent meeting</button> 489 - <button class="search-example-chip" type="button">something I learned</button> 489 + <button class="search-example-chip" type="button">something i learned</button> 490 490 <button class="search-example-chip" type="button">project ideas</button> 491 491 </div> 492 492 </div> ··· 681 681 let facetHtml = ` 682 682 <label class="filter-item ${!currentFacet ? 'active' : ''}"> 683 683 <input type="radio" name="facet" value="" ${!currentFacet ? 'checked' : ''}> 684 - <span class="filter-label">All</span> 684 + <span class="filter-label">all</span> 685 685 </label> 686 686 `; 687 687 ··· 718 718 let agentHtml = ` 719 719 <label class="filter-item ${!currentAgent ? 'active' : ''}"> 720 720 <input type="radio" name="agent" value="" ${!currentAgent ? 'checked' : ''}> 721 - <span class="filter-label">All</span> 721 + <span class="filter-label">all</span> 722 722 </label> 723 723 `; 724 724 for (const t of agents) { ··· 865 865 function loadMoreForDay(day, currentOffset, resultsDiv, btn) { 866 866 loadMoreController?.abort(); 867 867 loadMoreController = new AbortController(); 868 - btn.textContent = 'Loading...'; 868 + btn.textContent = 'loading...'; 869 869 btn.disabled = true; 870 870 871 871 const url = new URL(dayResultsUrl, window.location.origin); ··· 900 900 .catch(err => { 901 901 if (err.name === 'AbortError') return; 902 902 console.error('Load more error:', err); 903 - btn.textContent = 'Error - click to retry'; 903 + btn.textContent = 'error - click to retry'; 904 904 btn.disabled = false; 905 905 }); 906 906 }
+144 -144
apps/settings/workspace.html
··· 2007 2007 <!-- Setup prompt (shown when no facets) --> 2008 2008 <div class="setup-section" id="setupSection" style="display:none"> 2009 2009 <h3>welcome to solstone</h3> 2010 - <p>Create your first facet to start organizing your journal.</p> 2011 - <button class="setup-btn" id="createPersonalBtn">Create Personal Facet</button> 2010 + <p>create your first facet to start organizing your journal.</p> 2011 + <button class="setup-btn" id="createPersonalBtn">create personal facet</button> 2012 2012 </div> 2013 2013 2014 2014 <div class="settings-field"> 2015 2015 <label for="field-name">full name</label> 2016 - <input type="text" id="field-name" data-section="identity" data-key="name" placeholder="Your full name"> 2017 - <small>Your full legal or formal name</small> 2016 + <input type="text" id="field-name" data-section="identity" data-key="name" placeholder="your full name"> 2017 + <small>your full legal or formal name</small> 2018 2018 </div> 2019 2019 2020 2020 <div class="settings-field"> 2021 2021 <label for="field-preferred">preferred name</label> 2022 - <input type="text" id="field-preferred" data-section="identity" data-key="preferred" placeholder="Your preferred name"> 2023 - <small>Preferred name or nickname to be used when addressing you</small> 2022 + <input type="text" id="field-preferred" data-section="identity" data-key="preferred" placeholder="your preferred name"> 2023 + <small>preferred name or nickname to be used when addressing you</small> 2024 2024 </div> 2025 2025 2026 2026 <div class="settings-field"> 2027 2027 <label for="field-bio">bio</label> 2028 2028 <textarea id="field-bio" data-section="identity" data-key="bio" rows="2" placeholder="e.g., a software engineer interested in AI"></textarea> 2029 - <small>Brief description of yourself used in AI templates</small> 2029 + <small>brief description of yourself used in ai templates</small> 2030 2030 </div> 2031 2031 2032 2032 <div class="settings-field"> 2033 2033 <label for="field-pronouns">pronouns</label> 2034 2034 <select id="field-pronouns" data-section="identity" data-key="pronouns"> 2035 - <option value="">Select pronouns...</option> 2035 + <option value="">select pronouns...</option> 2036 2036 <option value="she/her/her/herself">she/her/hers/herself</option> 2037 2037 <option value="he/him/his/himself">he/him/his/himself</option> 2038 2038 <option value="they/them/their/themselves">they/them/their/themselves</option> ··· 2040 2040 <option value="xe/xem/xyr/xemself">xe/xem/xyrs/xemself</option> 2041 2041 <option value="it/it/its/itself">it/its/its/itself</option> 2042 2042 </select> 2043 - <small>Pronouns for use in templates (subject/object/possessive/reflexive)</small> 2043 + <small>pronouns for use in templates (subject/object/possessive/reflexive)</small> 2044 2044 </div> 2045 2045 2046 2046 <div class="settings-field"> 2047 2047 <label for="field-aliases">aliases</label> 2048 2048 <input type="text" id="field-aliases" data-section="identity" data-key="aliases" placeholder="e.g., nickname1, nickname2"> 2049 - <small>Comma-separated alternative names that may appear in transcripts</small> 2049 + <small>comma-separated alternative names that may appear in transcripts</small> 2050 2050 </div> 2051 2051 2052 2052 <div class="settings-field"> 2053 2053 <label for="field-emails">email addresses</label> 2054 2054 <input type="text" id="field-emails" data-section="identity" data-key="email_addresses" placeholder="e.g., work@company.com, personal@example.com"> 2055 - <small>Comma-separated email addresses for participant detection</small> 2055 + <small>comma-separated email addresses for participant detection</small> 2056 2056 </div> 2057 2057 2058 2058 <div class="settings-field"> ··· 2097 2097 <!-- Providers Section --> 2098 2098 <section class="settings-section" id="section-providers" role="tabpanel" aria-labelledby="tab-providers"> 2099 2099 <h2>providers</h2> 2100 - <p class="settings-section-desc">Configure AI providers separately for generation (text analysis) and cogitate (tool-calling agents).</p> 2100 + <p class="settings-section-desc">configure ai providers separately for generation (text analysis) and cogitate (tool-calling agents).</p> 2101 2101 2102 2102 <form class="settings-form" onsubmit="return false;"> 2103 2103 2104 2104 <h3 class="settings-subsection-title">generate</h3> 2105 - <p class="settings-subsection-desc">Text generation for analysis, extraction, and transcription tasks.</p> 2105 + <p class="settings-subsection-desc">text generation for analysis, extraction, and transcription tasks.</p> 2106 2106 <div class="provider-row"> 2107 2107 <div class="settings-field"> 2108 2108 <label for="field-generate-provider">provider</label> ··· 2114 2114 <div class="settings-field"> 2115 2115 <label for="field-generate-tier">tier</label> 2116 2116 <select id="field-generate-tier"> 2117 - <option value="1">Best - Most capable</option> 2118 - <option value="2">Average - Balanced</option> 2119 - <option value="3">Fast - Lightweight</option> 2117 + <option value="1">best - most capable</option> 2118 + <option value="2">average - balanced</option> 2119 + <option value="3">fast - lightweight</option> 2120 2120 </select> 2121 2121 <small></small> 2122 2122 </div> ··· 2131 2131 <div id="generateProviderKeyWarning" class="provider-key-warning" style="display:none"> 2132 2132 <span>&#9888;</span> 2133 2133 <span>API key not configured for generate provider.</span> 2134 - <a href="#" id="generateProviderKeyLink">Configure in API Keys</a> 2134 + <a href="#" id="generateProviderKeyLink">configure in API keys</a> 2135 2135 </div> 2136 2136 2137 2137 <h3 class="settings-subsection-title">cogitate</h3> 2138 - <p class="settings-subsection-desc">Tool-calling agents for interactive chat, daily briefings, and complex reasoning.</p> 2138 + <p class="settings-subsection-desc">tool-calling agents for interactive chat, daily briefings, and complex reasoning.</p> 2139 2139 <div class="provider-row"> 2140 2140 <div class="settings-field"> 2141 2141 <label for="field-cogitate-provider">provider</label> ··· 2147 2147 <div class="settings-field"> 2148 2148 <label for="field-cogitate-tier">tier</label> 2149 2149 <select id="field-cogitate-tier"> 2150 - <option value="1">Best - Most capable</option> 2151 - <option value="2">Average - Balanced</option> 2152 - <option value="3">Fast - Lightweight</option> 2150 + <option value="1">best - most capable</option> 2151 + <option value="2">average - balanced</option> 2152 + <option value="3">fast - lightweight</option> 2153 2153 </select> 2154 2154 <small></small> 2155 2155 </div> ··· 2165 2165 <div class="settings-field"> 2166 2166 <label for="field-cogitate-auth">auth</label> 2167 2167 <select id="field-cogitate-auth"> 2168 - <option value="platform">Platform Account</option> 2169 - <option value="api_key">API Key</option> 2168 + <option value="platform">platform account</option> 2169 + <option value="api_key">API key</option> 2170 2170 </select> 2171 - <small>How the CLI authenticates with the provider</small> 2171 + <small>how the CLI authenticates with the provider</small> 2172 2172 </div> 2173 2173 </div> 2174 2174 <h3 class="settings-subsection-title">google backend</h3> ··· 2177 2177 <div class="settings-field"> 2178 2178 <label for="field-google-backend">backend</label> 2179 2179 <select id="field-google-backend"> 2180 - <option value="auto">Auto-detect</option> 2180 + <option value="auto">auto-detect</option> 2181 2181 <option value="aistudio">AI Studio</option> 2182 2182 <option value="vertex">Vertex AI</option> 2183 2183 </select> 2184 - <small>Auto-detect probes your key on first use</small> 2184 + <small>auto-detect probes your key on first use</small> 2185 2185 </div> 2186 2186 </div> 2187 2187 <div id="vertexFields" style="display:none"> ··· 2192 2192 <span id="vertex-creds-email" style="color:#666;font-size:0.9em">not configured</span> 2193 2193 </div> 2194 2194 <div id="vertex-creds-actions"> 2195 - <button type="button" id="btn-vertex-creds-file" onclick="document.getElementById('vertex-creds-file-input').click()" style="margin-right:0.4em">Choose file</button> 2196 - <button type="button" id="btn-vertex-creds-paste" onclick="toggleVertexCredsPaste()">Paste JSON</button> 2197 - <button type="button" id="btn-vertex-creds-remove" onclick="removeVertexCreds()" style="display:none;color:#c33">Remove</button> 2195 + <button type="button" id="btn-vertex-creds-file" onclick="document.getElementById('vertex-creds-file-input').click()" style="margin-right:0.4em">choose file</button> 2196 + <button type="button" id="btn-vertex-creds-paste" onclick="toggleVertexCredsPaste()">paste JSON</button> 2197 + <button type="button" id="btn-vertex-creds-remove" onclick="removeVertexCreds()" style="display:none;color:#c33">remove</button> 2198 2198 </div> 2199 2199 <input type="file" id="vertex-creds-file-input" accept=".json" style="display:none" onchange="uploadVertexCredsFile(this)"> 2200 - <textarea id="vertex-creds-paste-input" rows="6" style="display:none;width:100%;margin-top:0.5em;font-family:monospace;font-size:0.85em" placeholder="Paste service account JSON here"></textarea> 2200 + <textarea id="vertex-creds-paste-input" rows="6" style="display:none;width:100%;margin-top:0.5em;font-family:monospace;font-size:0.85em" placeholder="paste service account JSON here"></textarea> 2201 2201 <div id="vertex-creds-paste-actions" style="display:none;margin-top:0.4em"> 2202 - <button type="button" onclick="submitVertexCredsPaste()">Save</button> 2203 - <button type="button" onclick="cancelVertexCredsPaste()" style="margin-left:0.4em">Cancel</button> 2202 + <button type="button" onclick="submitVertexCredsPaste()">save</button> 2203 + <button type="button" onclick="cancelVertexCredsPaste()" style="margin-left:0.4em">cancel</button> 2204 2204 </div> 2205 2205 <small>JSON credentials for Vertex AI authentication</small> 2206 2206 </div> ··· 2209 2209 <div id="cogitateProviderKeyWarning" class="provider-key-warning" style="display:none"> 2210 2210 <span>&#9888;</span> 2211 2211 <span>API key not configured for cogitate provider.</span> 2212 - <a href="#" id="cogitateProviderKeyLink">Configure in API Keys</a> 2212 + <a href="#" id="cogitateProviderKeyLink">configure in API keys</a> 2213 2213 </div> 2214 - <small id="providerStatus">Loading...</small> 2214 + <small id="providerStatus">loading...</small> 2215 2215 2216 2216 <!-- Detailed Tasks --> 2217 2217 <div class="context-overrides expanded" id="contextOverrides"> 2218 2218 <div class="context-overrides-header"> 2219 - <span class="context-overrides-title">Detailed Tasks</span> 2219 + <span class="context-overrides-title">detailed tasks</span> 2220 2220 </div> 2221 2221 <div class="context-overrides-body" id="contextOverridesBody"> 2222 - <p style="color:#666;font-size:0.9em;margin:0 0 1em 0">Override provider or tier for specific tasks. Unset values use defaults.</p> 2223 - <div id="contextGroups">Loading...</div> 2222 + <p style="color:#666;font-size:0.9em;margin:0 0 1em 0">override provider or tier for specific tasks. unset values use defaults.</p> 2223 + <div id="contextGroups">loading...</div> 2224 2224 </div> 2225 2225 </div> 2226 2226 </form> ··· 2229 2229 <!-- API Keys Section --> 2230 2230 <section class="settings-section" id="section-apikeys" role="tabpanel" aria-labelledby="tab-apikeys"> 2231 2231 <h2>API keys</h2> 2232 - <p class="settings-section-desc">Configure API keys for AI providers. Keys are stored in your journal config.</p> 2232 + <p class="settings-section-desc">configure API keys for AI providers. keys are stored in your journal config.</p> 2233 2233 2234 2234 <form class="settings-form" onsubmit="return false;"> 2235 2235 <div style="margin-bottom: 1em;"> 2236 - <button type="button" id="revalidateAllKeys" class="btn-secondary" onclick="revalidateAllKeys()">Re-validate All Keys</button> 2236 + <button type="button" id="revalidateAllKeys" class="btn-secondary" onclick="revalidateAllKeys()">re-validate all keys</button> 2237 2237 <span id="revalidateStatus" style="margin-left: 0.5em; font-size: 0.85em;"></span> 2238 2238 </div> 2239 2239 2240 2240 <div id="googleApiKeyField" class="settings-field"> 2241 2241 <label for="field-env-google">Google AI (Gemini)</label> 2242 2242 <div class="password-wrap"> 2243 - <input type="text" id="field-env-google" data-section="env" data-key="GOOGLE_API_KEY" placeholder="Enter API key" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2244 - <button type="button" class="password-toggle" data-toggle="field-env-google" title="Hide key"> 2243 + <input type="text" id="field-env-google" data-section="env" data-key="GOOGLE_API_KEY" placeholder="enter API key" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2244 + <button type="button" class="password-toggle" data-toggle="field-env-google" title="hide key"> 2245 2245 <span>&#128064;</span> 2246 2246 </button> 2247 2247 </div> 2248 - <small>Primary provider for transcription and insights. <a href="https://aistudio.google.com/apikey" target="_blank">Get key</a></small> 2248 + <small>primary provider for transcription and insights. <a href="https://aistudio.google.com/apikey" target="_blank">get key</a></small> 2249 2249 </div> 2250 2250 2251 2251 <div class="settings-field"> 2252 2252 <label for="field-env-openai">OpenAI</label> 2253 2253 <div class="password-wrap"> 2254 - <input type="text" id="field-env-openai" data-section="env" data-key="OPENAI_API_KEY" placeholder="Enter API key" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2255 - <button type="button" class="password-toggle" data-toggle="field-env-openai" title="Hide key"> 2254 + <input type="text" id="field-env-openai" data-section="env" data-key="OPENAI_API_KEY" placeholder="enter API key" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2255 + <button type="button" class="password-toggle" data-toggle="field-env-openai" title="hide key"> 2256 2256 <span>&#128064;</span> 2257 2257 </button> 2258 2258 </div> 2259 - <small>Alternative provider for chat and agents. <a href="https://platform.openai.com/api-keys" target="_blank">Get key</a></small> 2259 + <small>alternative provider for chat and agents. <a href="https://platform.openai.com/api-keys" target="_blank">get key</a></small> 2260 2260 </div> 2261 2261 2262 2262 <div class="settings-field"> 2263 2263 <label for="field-env-anthropic">Anthropic</label> 2264 2264 <div class="password-wrap"> 2265 - <input type="text" id="field-env-anthropic" data-section="env" data-key="ANTHROPIC_API_KEY" placeholder="Enter API key" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2266 - <button type="button" class="password-toggle" data-toggle="field-env-anthropic" title="Hide key"> 2265 + <input type="text" id="field-env-anthropic" data-section="env" data-key="ANTHROPIC_API_KEY" placeholder="enter API key" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2266 + <button type="button" class="password-toggle" data-toggle="field-env-anthropic" title="hide key"> 2267 2267 <span>&#128064;</span> 2268 2268 </button> 2269 2269 </div> 2270 - <small>Alternative provider for chat and agents. <a href="https://console.anthropic.com/settings/keys" target="_blank">Get key</a></small> 2270 + <small>alternative provider for chat and agents. <a href="https://console.anthropic.com/settings/keys" target="_blank">get key</a></small> 2271 2271 </div> 2272 2272 2273 2273 <div class="settings-field"> 2274 2274 <label for="field-env-revai">Rev.ai</label> 2275 2275 <div class="password-wrap"> 2276 - <input type="text" id="field-env-revai" data-section="env" data-key="REVAI_ACCESS_TOKEN" placeholder="Enter access token" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2277 - <button type="button" class="password-toggle" data-toggle="field-env-revai" title="Hide token"> 2276 + <input type="text" id="field-env-revai" data-section="env" data-key="REVAI_ACCESS_TOKEN" placeholder="enter access token" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2277 + <button type="button" class="password-toggle" data-toggle="field-env-revai" title="hide token"> 2278 2278 <span>&#128064;</span> 2279 2279 </button> 2280 2280 </div> 2281 - <small>Audio transcription for imported files. <a href="https://www.rev.ai/access_token" target="_blank">Get token</a></small> 2281 + <small>audio transcription for imported files. <a href="https://www.rev.ai/access_token" target="_blank">get token</a></small> 2282 2282 </div> 2283 2283 <div class="settings-field"> 2284 2284 <label for="field-env-plaud">Plaud</label> 2285 2285 <div class="password-wrap"> 2286 - <input type="text" id="field-env-plaud" data-section="env" data-key="PLAUD_ACCESS_TOKEN" placeholder="Enter access token" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2287 - <button type="button" class="password-toggle" data-toggle="field-env-plaud" title="Hide token"> 2286 + <input type="text" id="field-env-plaud" data-section="env" data-key="PLAUD_ACCESS_TOKEN" placeholder="enter access token" autocomplete="off" data-1p-ignore data-lpignore="true" data-bwignore="true"> 2287 + <button type="button" class="password-toggle" data-toggle="field-env-plaud" title="hide token"> 2288 2288 <span>&#128064;</span> 2289 2289 </button> 2290 2290 </div> ··· 2313 2313 <div id="backendKeyWarning" class="provider-key-warning" style="display:none"> 2314 2314 <span>&#9888;</span> 2315 2315 <span id="backendKeyWarningText">API key not configured.</span> 2316 - <a href="#" id="backendKeyLink">Configure in API Keys</a> 2316 + <a href="#" id="backendKeyLink">configure in API keys</a> 2317 2317 </div> 2318 2318 2319 2319 <!-- Whisper-specific settings --> ··· 2421 2421 <input type="checkbox" id="field-transcribe-preserve" data-section="transcribe" data-key="preserve_all"> 2422 2422 <span class="slider"></span> 2423 2423 </label> 2424 - <label for="field-transcribe-preserve" class="toggle-label" style="cursor: pointer;">preserve silent recordings</label> 2424 + <label for="field-transcribe-preserve" class="toggle-label" style="cursor: pointer;">preserve silent audio</label> 2425 2425 </div> 2426 2426 <small>Keep audio files even when no speech is detected</small> 2427 2427 </div> ··· 2434 2434 </label> 2435 2435 <label for="field-transcribe-noise-upgrade" class="toggle-label" style="cursor: pointer;">noise upgrade</label> 2436 2436 </div> 2437 - <small>Automatically use Rev.ai for noisy recordings (requires Rev.ai token)</small> 2437 + <small>automatically use Rev.ai for noisy audio (requires Rev.ai token)</small> 2438 2438 </div> 2439 2439 </form> 2440 2440 </section> ··· 2459 2459 <input type="checkbox" id="field-tmux-enabled"> 2460 2460 <span class="slider"></span> 2461 2461 </label> 2462 - <span style="color: #666; font-size: 0.9em;">Take in tmux sessions when screen is idle</span> 2462 + <span style="color: #666; font-size: 0.9em;">take in tmux sessions when screen is idle</span> 2463 2463 </div> 2464 2464 </div> 2465 2465 ··· 2491 2491 <!-- Redaction Rules --> 2492 2492 <div class="context-overrides expanded" id="visionRedaction"> 2493 2493 <div class="context-overrides-header" id="visionRedactionHeader"> 2494 - <span class="context-overrides-title">Redaction Rules</span> 2494 + <span class="context-overrides-title">redaction rules</span> 2495 2495 <span class="context-overrides-toggle">&#9660;</span> 2496 2496 </div> 2497 2497 <div class="context-overrides-body"> 2498 - <p style="color:#666;font-size:0.9em;margin:0 0 1em 0">Instructions the AI follows to redact sensitive content from screen analysis. Double-click a rule to edit it.</p> 2498 + <p style="color:#666;font-size:0.9em;margin:0 0 1em 0">instructions the ai follows to redact sensitive content from screen analysis. double-click a rule to edit it.</p> 2499 2499 <div class="redact-list" id="redactRulesList"></div> 2500 2500 <div class="redact-add-row"> 2501 - <input type="text" class="redact-add-input" id="redactAddInput" placeholder="Add a redaction rule..." maxlength="200"> 2502 - <button class="redact-add-btn" id="redactAddBtn">+ Add</button> 2501 + <input type="text" class="redact-add-input" id="redactAddInput" placeholder="add a redaction rule..." maxlength="200"> 2502 + <button class="redact-add-btn" id="redactAddBtn">+ add</button> 2503 2503 </div> 2504 2504 <div class="redact-validation" id="redactValidation" style="display:none"></div> 2505 2505 </div> ··· 2592 2592 <div class="settings-field"> 2593 2593 <label for="field-password">web password</label> 2594 2594 <div class="password-wrap"> 2595 - <input type="password" id="field-password" data-section="convey" data-key="password" placeholder="Enter password to protect web access"> 2596 - <button type="button" class="password-toggle" id="passwordToggle" title="Show password"> 2595 + <input type="password" id="field-password" data-section="convey" data-key="password" placeholder="enter password to protect web access"> 2596 + <button type="button" class="password-toggle" id="passwordToggle" title="show password"> 2597 2597 <span id="passwordToggleIcon">&#128065;</span> 2598 2598 </button> 2599 2599 </div> ··· 2605 2605 <!-- Sync Section --> 2606 2606 <section class="settings-section" id="section-sync" role="tabpanel" aria-labelledby="tab-sync"> 2607 2607 <h2>sync</h2> 2608 - <p class="settings-section-desc">Configure automatic syncing of recordings from external devices.</p> 2608 + <p class="settings-section-desc">configure automatic syncing of observations from external devices.</p> 2609 2609 <div id="syncLoadState" class="settings-load-state" aria-live="polite"></div> 2610 2610 2611 2611 <form class="settings-form" onsubmit="return false;"> ··· 2613 2613 <div id="plaudSyncCard"> 2614 2614 <h3 class="settings-subsection-title">Plaud</h3> 2615 2615 <p class="settings-subsection-desc"> 2616 - Automatically sync and import recordings from your Plaud device every hour. 2616 + automatically sync and import observations from your Plaud device every hour. 2617 2617 </p> 2618 2618 2619 2619 <div class="settings-field" id="plaudSyncField"> ··· 2623 2623 <input type="checkbox" id="field-plaud-sync-enabled"> 2624 2624 <span class="slider"></span> 2625 2625 </label> 2626 - <span style="color: #666; font-size: 0.9em;">Sync and import Plaud recordings every hour</span> 2626 + <span style="color: #666; font-size: 0.9em;">sync and import Plaud observations every hour</span> 2627 2627 </div> 2628 2628 </div> 2629 2629 2630 2630 <p id="plaudSyncTokenNote" style="display: none; color: #888; font-size: 0.85em; margin-top: 1em; padding: 0.75em; background: #f8f9fa; border-radius: 4px;"> 2631 - Configure your Plaud token in <a href="#apikeys" style="color: #667eea;">API Keys</a> to enable sync. 2631 + configure your Plaud token in <a href="#apikeys" style="color: #667eea;">API keys</a> to enable sync. 2632 2632 </p> 2633 2633 </div> 2634 2634 2635 2635 <div id="granolaSyncCard"> 2636 2636 <h3 class="settings-subsection-title">Granola (via muesli)</h3> 2637 2637 <p class="settings-subsection-desc"> 2638 - Automatically sync new meeting transcripts from Granola. 2638 + automatically sync new meeting transcripts from Granola. 2639 2639 </p> 2640 2640 2641 2641 <div class="settings-field" id="granolaSyncField"> ··· 2645 2645 <input type="checkbox" id="field-granola-sync-enabled"> 2646 2646 <span class="slider"></span> 2647 2647 </label> 2648 - <span style="color: #666; font-size: 0.9em;">Check for new transcripts every hour</span> 2648 + <span style="color: #666; font-size: 0.9em;">check for new transcripts every hour</span> 2649 2649 </div> 2650 2650 </div> 2651 2651 </div> ··· 2653 2653 <div id="obsidianSyncCard"> 2654 2654 <h3 class="settings-subsection-title">Obsidian vault</h3> 2655 2655 <p class="settings-subsection-desc"> 2656 - Automatically sync new and updated notes from your vault. 2656 + automatically sync new and updated notes from your vault. 2657 2657 </p> 2658 2658 2659 2659 <div class="settings-field" id="obsidianSyncField"> ··· 2663 2663 <input type="checkbox" id="field-obsidian-sync-enabled"> 2664 2664 <span class="slider"></span> 2665 2665 </label> 2666 - <span style="color: #666; font-size: 0.9em;">Check for changes every hour</span> 2666 + <span style="color: #666; font-size: 0.9em;">check for changes every hour</span> 2667 2667 </div> 2668 2668 </div> 2669 2669 </div> ··· 2673 2673 <!-- Storage Section --> 2674 2674 <section class="settings-section" id="section-storage" role="tabpanel" aria-labelledby="tab-storage"> 2675 2675 <h2>your observed media (raw audio and screencast files)</h2> 2676 - <p class="settings-section-desc">Manage raw media retention and view storage usage.</p> 2676 + <p class="settings-section-desc">manage raw media retention and view storage usage.</p> 2677 2677 <div id="storageLoadState" class="settings-load-state" aria-live="polite"></div> 2678 2678 <div id="storageWarnings"></div> 2679 2679 ··· 2683 2683 <div class="storage-summary-grid"> 2684 2684 <div> 2685 2685 <div class="storage-stat-value" id="storageSummaryRaw">—</div> 2686 - <div class="storage-stat-label">Raw Media</div> 2686 + <div class="storage-stat-label">raw media</div> 2687 2687 </div> 2688 2688 <div> 2689 2689 <div class="storage-stat-value" id="storageSummaryDerived">—</div> 2690 - <div class="storage-stat-label">Derived Content</div> 2690 + <div class="storage-stat-label">derived content</div> 2691 2691 </div> 2692 2692 <div> 2693 2693 <div class="storage-stat-value" id="storageSummarySegments">—</div> 2694 - <div class="storage-stat-label">Segments</div> 2694 + <div class="storage-stat-label">segments</div> 2695 2695 </div> 2696 2696 </div> 2697 2697 <div style="margin-top: 0.75em; font-size: 0.8em; color: #888; text-align: center;" id="storageSummaryDetail"></div> ··· 2699 2699 2700 2700 <div class="settings-field" id="retentionModeField"> 2701 2701 <label>retention mode</label> 2702 - <small>Controls how long raw media files (audio, video, screen diffs) are kept after processing. <a href="https://solpbc.org/privacy#recording-laws" target="_blank" rel="noopener">recording-consent laws</a> may apply in your jurisdiction.</small> 2702 + <small>controls how long raw media files (audio, video, screen diffs) are kept after processing. <a href="https://solpbc.org/privacy#recording-laws" target="_blank" rel="noopener">recording-consent laws</a> may apply in your jurisdiction.</small> 2703 2703 <div id="retentionDaysField" class="retention-days-row" style="margin-top: 0.5em;"> 2704 2704 <button type="button" class="settings-preset-btn" data-days="7">7</button> 2705 2705 <button type="button" class="settings-preset-btn" data-days="30">30</button> 2706 2706 <button type="button" class="settings-preset-btn" data-days="90">90</button> 2707 - <input type="number" id="retentionDaysInput" min="1" max="365" style="width: 5em; padding: 0.4em 0.6em; border: 1px solid #ccc; border-radius: 4px;" placeholder="Custom"> 2707 + <input type="number" id="retentionDaysInput" min="1" max="365" style="width: 5em; padding: 0.4em 0.6em; border: 1px solid #ccc; border-radius: 4px;" placeholder="custom"> 2708 2708 <span style="color: #666; font-size: 0.9em;">days</span> 2709 2709 </div> 2710 2710 <label for="retentionDontRetain" style="display: flex; align-items: center; gap: 0.5em; margin-top: 0.75em; color: #666; font-size: 0.9em;"> ··· 2716 2716 2717 2717 <div class="context-overrides" id="streamOverridesContainer" style="display: none;"> 2718 2718 <div class="context-overrides-header" id="streamOverridesToggle" style="cursor: pointer;"> 2719 - <span class="context-overrides-title">Per-Stream Overrides</span> 2719 + <span class="context-overrides-title">per-stream overrides</span> 2720 2720 <span class="context-overrides-toggle">&#9660;</span> 2721 2721 </div> 2722 2722 <div class="context-overrides-body" id="streamOverridesBody"> 2723 - <p style="color: #666; font-size: 0.85em; margin: 0 0 1em 0;">Override the global retention mode for individual streams.</p> 2723 + <p style="color: #666; font-size: 0.85em; margin: 0 0 1em 0;">override the global retention mode for individual streams.</p> 2724 2724 <div id="streamOverridesList"></div> 2725 2725 </div> 2726 2726 </div> 2727 2727 2728 2728 <div class="settings-danger-section"> 2729 - <button type="button" id="cleanupNowBtn" class="action-button-danger">Clean Up Now</button> 2730 - <small style="display: block; margin-top: 0.5em; color: #666;">Manually purge raw media older than a specified number of days.</small> 2729 + <button type="button" id="cleanupNowBtn" class="action-button-danger">clean up now</button> 2730 + <small style="display: block; margin-top: 0.5em; color: #666;">manually purge raw media older than a specified number of days.</small> 2731 2731 </div> 2732 2732 </form> 2733 2733 </section> ··· 2812 2812 <div class="settings-field"> 2813 2813 <label>named date</label> 2814 2814 <span id="agent-named-date" class="field-display">—</span> 2815 - <small>When the name was last changed</small> 2815 + <small>when the name was last changed</small> 2816 2816 </div> 2817 2817 2818 2818 <div class="settings-field"> 2819 - <button class="setup-btn" id="resetAgentName" style="background: #6c757d; font-size: 0.85em; padding: 0.4em 1em;">Reset to Default</button> 2820 - <small>Reset the agent name back to "sol"</small> 2819 + <button class="setup-btn" id="resetAgentName" style="background: #6c757d; font-size: 0.85em; padding: 0.4em 1em;">reset to default</button> 2820 + <small>reset the agent name back to "sol"</small> 2821 2821 </div> 2822 2822 </form> 2823 2823 </section> ··· 2827 2827 <div class="facet-header"> 2828 2828 <h2>appearance</h2> 2829 2829 <div class="toggle-container"> 2830 - <span class="toggle-label" id="toggleLabel">Active</span> 2830 + <span class="toggle-label" id="toggleLabel">active</span> 2831 2831 <label class="toggle-switch"> 2832 2832 <input type="checkbox" id="mutedToggle"> 2833 2833 <span class="slider"></span> 2834 2834 </label> 2835 2835 </div> 2836 2836 </div> 2837 - <p class="settings-section-desc">Customize how this facet appears in the interface.</p> 2837 + <p class="settings-section-desc">customize how this facet appears in the interface.</p> 2838 2838 2839 2839 <div class="settings-field"> 2840 2840 <label for="facetTitleInput">title</label> 2841 2841 <div class="editable-field" id="facetTitleField"> 2842 - <span class="field-display" id="facetTitleDisplay">Loading...</span> 2842 + <span class="field-display" id="facetTitleDisplay">loading...</span> 2843 2843 <input type="text" class="field-input" id="facetTitleInput" style="display:none"> 2844 2844 </div> 2845 - <small id="facetTitleField-hint">Double-click or press Enter to edit</small> 2845 + <small id="facetTitleField-hint">double-click or press Enter to edit</small> 2846 2846 </div> 2847 2847 2848 2848 <div class="settings-field"> 2849 2849 <label for="facetDescInput">description</label> 2850 2850 <div class="editable-field" id="facetDescField"> 2851 - <span class="field-display" id="facetDescDisplay">No description set</span> 2851 + <span class="field-display" id="facetDescDisplay">no description set</span> 2852 2852 <textarea class="field-input" id="facetDescInput" rows="3" style="display:none"></textarea> 2853 2853 </div> 2854 - <small id="facetDescField-hint">Double-click or press Enter to edit</small> 2854 + <small id="facetDescField-hint">double-click or press Enter to edit</small> 2855 2855 </div> 2856 2856 2857 2857 <div class="settings-field"> 2858 2858 <label for="emojiInput">icon</label> 2859 2859 <div class="emoji-display" id="emojiDisplay" tabindex="0" role="button">&#128230;</div> 2860 - <input type="text" id="emojiInput" class="emoji-input" maxlength="4" placeholder="Paste emoji" style="display:none"> 2861 - <small>Click to change emoji</small> 2860 + <input type="text" id="emojiInput" class="emoji-input" maxlength="4" placeholder="paste emoji" style="display:none"> 2861 + <small>click to change emoji</small> 2862 2862 </div> 2863 2863 2864 2864 <div class="settings-field"> ··· 2867 2867 <div class="color-preview" id="colorPreview"></div> 2868 2868 <span id="colorHexDisplay">#667eea</span> 2869 2869 </div> 2870 - <small>Click to choose a color</small> 2870 + <small>click to choose a color</small> 2871 2871 </div> 2872 2872 </section> 2873 2873 ··· 2879 2879 <!-- Attached Activities --> 2880 2880 <div class="activities-section"> 2881 2881 <div class="activities-header"> 2882 - <span class="activities-title">Attached Activities</span> 2882 + <span class="activities-title">attached activities</span> 2883 2883 <span class="activities-count" id="attachedActivitiesCount"></span> 2884 2884 </div> 2885 2885 <div class="activities-list" id="attachedActivitiesList"> 2886 - <div class="activities-empty">No activities attached yet</div> 2886 + <div class="activities-empty">no activities attached yet</div> 2887 2887 </div> 2888 2888 </div> 2889 2889 2890 2890 <!-- Add Activities --> 2891 2891 <div class="activities-section" style="margin-top: 1.5em;"> 2892 2892 <div class="activities-header"> 2893 - <span class="activities-title">Add Activity</span> 2893 + <span class="activities-title">add activity</span> 2894 2894 </div> 2895 2895 <div class="activities-add-panel"> 2896 2896 <div class="activities-defaults" id="defaultActivitiesList"> 2897 - Loading... 2897 + loading... 2898 2898 </div> 2899 2899 <div class="activities-custom"> 2900 - <button class="activities-add-custom-btn" id="addCustomActivityBtn">+ Add Custom Activity</button> 2900 + <button class="activities-add-custom-btn" id="addCustomActivityBtn">+ add custom activity</button> 2901 2901 </div> 2902 2902 </div> 2903 2903 </div> ··· 2909 2909 <h3 id="customActivityHeading">add custom activity</h3> 2910 2910 <div class="settings-field"> 2911 2911 <label for="customActivityName">name</label> 2912 - <input type="text" id="customActivityName" placeholder="e.g., Research"> 2912 + <input type="text" id="customActivityName" placeholder="e.g., research"> 2913 2913 </div> 2914 2914 <div class="settings-field"> 2915 2915 <label for="customActivityDesc">description</label> 2916 - <textarea id="customActivityDesc" rows="2" placeholder="Brief description of this activity"></textarea> 2916 + <textarea id="customActivityDesc" rows="2" placeholder="brief description of this activity"></textarea> 2917 2917 </div> 2918 2918 <div class="settings-field"> 2919 2919 <label for="customActivityIcon">icon (emoji)</label> ··· 2921 2921 </div> 2922 2922 <div class="settings-field"> 2923 2923 <label for="customActivityInstructions">instructions <span style="font-weight:normal;color:#888;">(optional)</span></label> 2924 - <textarea id="customActivityInstructions" rows="2" placeholder="Detection hints and level guidance for the AI agent"></textarea> 2924 + <textarea id="customActivityInstructions" rows="2" placeholder="detection hints and level guidance for the ai agent"></textarea> 2925 2925 </div> 2926 - <button class="save-color-btn" id="saveCustomActivityBtn">Add Activity</button> 2926 + <button class="save-color-btn" id="saveCustomActivityBtn">add activity</button> 2927 2927 </div> 2928 2928 </div> 2929 2929 </section> ··· 2931 2931 <!-- Facet Activity Section --> 2932 2932 <section class="settings-section" id="section-facet-activity" data-requires-facet role="tabpanel" aria-labelledby="tab-facet-activity"> 2933 2933 <h2>activity log</h2> 2934 - <p class="settings-section-desc">Recent actions and changes to this facet.</p> 2934 + <p class="settings-section-desc">recent actions and changes to this facet.</p> 2935 2935 2936 2936 <div class="log-panel"> 2937 - <div class="log-header">Action Log</div> 2937 + <div class="log-header">action log</div> 2938 2938 <div class="log-entries" id="logEntries"> 2939 - <div class="log-empty">Loading...</div> 2939 + <div class="log-empty">loading...</div> 2940 2940 </div> 2941 2941 <div class="log-more" id="logMore" style="display:none"> 2942 - <button class="log-more-btn" id="logMoreBtn">Load older</button> 2942 + <button class="log-more-btn" id="logMoreBtn">load older</button> 2943 2943 </div> 2944 2944 </div> 2945 2945 </section> ··· 2964 2964 <h4>preview</h4> 2965 2965 <div class="modal-preview-pill" id="modalPreview"> 2966 2966 <span class="modal-preview-emoji" id="modalPreviewEmoji">&#128230;</span> 2967 - <span class="modal-preview-title" id="modalPreviewTitle">Facet</span> 2967 + <span class="modal-preview-title" id="modalPreviewTitle">facet</span> 2968 2968 </div> 2969 2969 </div> 2970 - <button class="save-color-btn" id="saveColorBtn">Apply Color</button> 2970 + <button class="save-color-btn" id="saveColorBtn">apply color</button> 2971 2971 </div> 2972 2972 </div> 2973 2973 ··· 2976 2976 <div class="color-modal-content" style="max-width: 480px;"> 2977 2977 <span class="color-close" id="cleanupModalClose">&times;</span> 2978 2978 <h3 id="cleanupHeading">clean up raw media</h3> 2979 - <p style="color: #666; font-size: 0.9em; margin-bottom: 1em;">Delete raw media files (audio, video, screen-frame originals) from segments older than the specified number of days.</p> 2979 + <p style="color: #666; font-size: 0.9em; margin-bottom: 1em;">delete raw media files (audio, video, screen-frame originals) from segments older than the specified number of days.</p> 2980 2980 2981 2981 <div class="settings-field" style="margin-bottom: 1em;"> 2982 2982 <label for="cleanupDaysInput">older than</label> ··· 2989 2989 <div class="settings-field" style="margin-bottom: 1em;"> 2990 2990 <label for="cleanupStreamFilter">stream filter (optional)</label> 2991 2991 <select id="cleanupStreamFilter" style="padding: 0.4em 0.6em; border: 1px solid #ccc; border-radius: 4px; width: 100%;"> 2992 - <option value="">All streams</option> 2992 + <option value="">all streams</option> 2993 2993 </select> 2994 2994 </div> 2995 2995 2996 2996 <div id="cleanupPreview" style="background: #f8f8f8; border-radius: 6px; padding: 1em; margin-bottom: 1em; display: none;"> 2997 - <div style="font-weight: 600; margin-bottom: 0.5em;">Preview</div> 2997 + <div style="font-weight: 600; margin-bottom: 0.5em;">preview</div> 2998 2998 <div id="cleanupPreviewText" style="font-size: 0.9em; color: #666;"></div> 2999 2999 </div> 3000 3000 3001 3001 <div style="display: flex; gap: 0.75em; justify-content: flex-end;"> 3002 - <button type="button" id="cleanupPreviewBtn" class="cleanup-preview-btn">Preview</button> 3003 - <button type="button" id="cleanupConfirmBtn" class="cleanup-confirm-btn" disabled>Delete Files</button> 3002 + <button type="button" id="cleanupPreviewBtn" class="cleanup-preview-btn">preview</button> 3003 + <button type="button" id="cleanupConfirmBtn" class="cleanup-confirm-btn" disabled>delete files</button> 3004 3004 </div> 3005 3005 </div> 3006 3006 </div> ··· 3182 3182 btn.className = 'setup-btn'; 3183 3183 btn.style.fontSize = '0.85em'; 3184 3184 btn.style.padding = '0.3em 0.8em'; 3185 - btn.textContent = 'Unmute'; 3185 + btn.textContent = 'unmute'; 3186 3186 btn.onclick = async () => { 3187 3187 btn.disabled = true; 3188 - btn.textContent = 'Unmuting...'; 3188 + btn.textContent = 'unmuting...'; 3189 3189 try { 3190 3190 const res = await fetch(`/app/settings/api/facet/${facet.name}`, { 3191 3191 method: 'PUT', ··· 3201 3201 notifySuccess('Unmuted', `${facet.title} will appear in the facet bar on reload.`); 3202 3202 } catch (err) { 3203 3203 btn.disabled = false; 3204 - btn.textContent = 'Unmute'; 3204 + btn.textContent = 'unmute'; 3205 3205 notifyError('Error', 'Failed to unmute facet'); 3206 3206 } 3207 3207 }; ··· 3756 3756 3757 3757 // Set status text and class 3758 3758 if (type === 'saved') { 3759 - small.textContent = 'Saved'; 3759 + small.textContent = 'saved'; 3760 3760 small.classList.add('status-saved'); 3761 3761 } else { 3762 3762 small.textContent = message || 'Error'; ··· 4054 4054 .catch(err => { 4055 4055 console.error('Error loading logs:', err); 4056 4056 if (!cursor) { 4057 - document.getElementById('logEntries').innerHTML = '<div class="log-empty">Error loading logs</div>'; 4057 + document.getElementById('logEntries').innerHTML = '<div class="log-empty">error loading logs</div>'; 4058 4058 } 4059 4059 }); 4060 4060 } ··· 4109 4109 document.getElementById('createPersonalBtn').onclick = async () => { 4110 4110 const btn = document.getElementById('createPersonalBtn'); 4111 4111 btn.disabled = true; 4112 - btn.textContent = 'Creating...'; 4112 + btn.textContent = 'creating...'; 4113 4113 4114 4114 try { 4115 4115 const response = await fetch('/app/settings/api/facet', { ··· 4127 4127 } catch (err) { 4128 4128 console.error('Error creating facet:', err); 4129 4129 btn.disabled = false; 4130 - btn.textContent = 'Create Personal Facet'; 4130 + btn.textContent = 'create personal facet'; 4131 4131 notifyError( 'Creation Failed', err.message); 4132 4132 } 4133 4133 }; ··· 4207 4207 populateProviders(providersData); 4208 4208 } catch (err) { 4209 4209 console.error('Error loading providers:', err); 4210 - document.getElementById('providerStatus').textContent = 'Error loading providers'; 4210 + document.getElementById('providerStatus').textContent = 'error loading providers'; 4211 4211 } 4212 4212 } 4213 4213 ··· 4332 4332 const btn = document.getElementById('revalidateAllKeys'); 4333 4333 const status = document.getElementById('revalidateStatus'); 4334 4334 btn.disabled = true; 4335 - status.textContent = 'Validating...'; 4335 + status.textContent = 'validating...'; 4336 4336 status.className = 'key-status-validating'; 4337 4337 try { 4338 4338 const response = await fetch('api/validate-keys', { method: 'POST' }); ··· 4361 4361 } 4362 4362 } 4363 4363 } 4364 - status.textContent = 'Done'; 4364 + status.textContent = 'done'; 4365 4365 status.className = 'key-status-valid'; 4366 4366 setTimeout(() => { status.textContent = ''; }, 2000); 4367 4367 } catch (err) { 4368 - status.textContent = 'Error: ' + err.message; 4368 + status.textContent = 'error: ' + err.message; 4369 4369 status.className = 'key-status-invalid'; 4370 4370 } finally { 4371 4371 btn.disabled = false; ··· 4430 4430 // Action area (reset button or save indicator) 4431 4431 html += `<span class="context-action" data-pattern="${item.pattern}">`; 4432 4432 if (hasOverride) { 4433 - html += `<button class="context-reset" data-pattern="${item.pattern}" title="Reset to default">&times;</button>`; 4433 + html += `<button class="context-reset" data-pattern="${item.pattern}" title="reset to default">&times;</button>`; 4434 4434 } 4435 4435 html += `</span>`; 4436 4436 ··· 4510 4510 if (saved) saved.classList.add('fade'); 4511 4511 setTimeout(() => { 4512 4512 if (hasOverride) { 4513 - actionArea.innerHTML = `<button class="context-reset" data-pattern="${pattern}" title="Reset to default">&times;</button>`; 4513 + actionArea.innerHTML = `<button class="context-reset" data-pattern="${pattern}" title="reset to default">&times;</button>`; 4514 4514 // Re-attach click handler for the new reset button 4515 4515 actionArea.querySelector('.context-reset')?.addEventListener('click', () => { 4516 4516 saveContextOverride(pattern, null, null, true); ··· 5161 5161 populateVision(visionData); 5162 5162 } catch (err) { 5163 5163 console.error('Error loading vision:', err); 5164 - document.getElementById('visionCategoryGroups').textContent = 'Error loading categories'; 5164 + document.getElementById('visionCategoryGroups').textContent = 'error loading categories'; 5165 5165 } 5166 5166 } 5167 5167 ··· 5218 5218 5219 5219 // Move buttons 5220 5220 html += `<div class="vision-move-buttons">`; 5221 - html += `<button class="vision-move-btn vision-move-up" data-category="${item.name}" title="Move up" ${isFirst ? 'disabled' : ''}>↑</button>`; 5222 - html += `<button class="vision-move-btn vision-move-down" data-category="${item.name}" title="Move down" ${isLast ? 'disabled' : ''}>↓</button>`; 5221 + html += `<button class="vision-move-btn vision-move-up" data-category="${item.name}" title="move up" ${isFirst ? 'disabled' : ''}>↑</button>`; 5222 + html += `<button class="vision-move-btn vision-move-down" data-category="${item.name}" title="move down" ${isLast ? 'disabled' : ''}>↓</button>`; 5223 5223 html += `</div>`; 5224 5224 5225 5225 // Reset button 5226 5226 html += `<span class="context-action" data-category="${item.name}">`; 5227 5227 if (override.importance || override.extraction) { 5228 - html += `<button class="context-reset vision-reset" data-category="${item.name}" title="Reset to default">&times;</button>`; 5228 + html += `<button class="context-reset vision-reset" data-category="${item.name}" title="reset to default">&times;</button>`; 5229 5229 } 5230 5230 html += `</span>`; 5231 5231 ··· 5336 5336 display.classList.remove('vision-extraction-empty'); 5337 5337 display.classList.add('vision-extraction-default'); 5338 5338 } else { 5339 - display.textContent = 'No extraction guidance'; 5339 + display.textContent = 'no extraction guidance'; 5340 5340 display.classList.remove('vision-extraction-default'); 5341 5341 display.classList.add('vision-extraction-empty'); 5342 5342 } ··· 5454 5454 <div class="redact-item" data-index="${index}"> 5455 5455 <span class="redact-text" data-index="${index}">${escapeHtml(rule)}</span> 5456 5456 <input type="text" class="redact-input" data-index="${index}" value="${escapeHtml(rule)}" maxlength="200" style="display:none"> 5457 - <button class="redact-remove" data-index="${index}" title="Remove">&times;</button> 5457 + <button class="redact-remove" data-index="${index}" title="remove">&times;</button> 5458 5458 </div> 5459 5459 `).join(''); 5460 5460 ··· 5616 5616 populateInsights(insightsData); 5617 5617 } catch (err) { 5618 5618 console.error('Error loading insights:', err); 5619 - document.getElementById('segmentInsightsList').textContent = 'Error loading insights'; 5620 - document.getElementById('dailyInsightsList').textContent = 'Error loading insights'; 5619 + document.getElementById('segmentInsightsList').textContent = 'error loading insights'; 5620 + document.getElementById('dailyInsightsList').textContent = 'error loading insights'; 5621 5621 } 5622 5622 } 5623 5623 ··· 5767 5767 renderActivities(activitiesData); 5768 5768 } catch (err) { 5769 5769 console.error('Error loading activities:', err); 5770 - document.getElementById('attachedActivitiesList').innerHTML = '<div class="activities-empty">Error loading activities</div>'; 5770 + document.getElementById('attachedActivitiesList').innerHTML = '<div class="activities-empty">error loading activities</div>'; 5771 5771 } 5772 5772 } 5773 5773 ··· 5790 5790 <span class="activity-icon">${a.icon || '📌'}</span> 5791 5791 <div class="activity-info"> 5792 5792 <div class="activity-name">${escapeHtml(a.name)}</div> 5793 - <div class="activity-desc activity-desc-editable" data-id="${a.id}" title="Double-click to edit">${escapeHtml(a.description) || '<em>No description</em>'}</div> 5794 - <div class="activity-instructions activity-instructions-editable" data-id="${a.id}" title="Double-click to edit instructions" style="font-size:0.8em;color:#888;margin-top:2px;">${a.instructions ? escapeHtml(a.instructions) : '<em>No instructions</em>'}</div> 5793 + <div class="activity-desc activity-desc-editable" data-id="${a.id}" title="double-click to edit">${escapeHtml(a.description) || '<em>no description</em>'}</div> 5794 + <div class="activity-instructions activity-instructions-editable" data-id="${a.id}" title="double-click to edit instructions" style="font-size:0.8em;color:#888;margin-top:2px;">${a.instructions ? escapeHtml(a.instructions) : '<em>no instructions</em>'}</div> 5795 5795 </div> 5796 5796 <div class="activity-controls"> 5797 5797 <select class="activity-priority" data-id="${a.id}"> ··· 5799 5799 <option value="normal" ${a.priority === 'normal' ? 'selected' : ''}>Normal</option> 5800 5800 <option value="low" ${a.priority === 'low' ? 'selected' : ''}>Low</option> 5801 5801 </select> 5802 - <button class="activity-remove" data-id="${a.id}" title="Remove">&times;</button> 5802 + <button class="activity-remove" data-id="${a.id}" title="remove">&times;</button> 5803 5803 </div> 5804 5804 </div> 5805 5805 `).join(''); ··· 5818 5818 defaultsList.innerHTML = defaults.map(d => { 5819 5819 const isAttached = attachedIds.has(d.id); 5820 5820 return ` 5821 - <span class="activity-chip ${isAttached ? 'activity-chip-attached' : ''}" data-id="${d.id}" ${isAttached ? '' : 'tabindex="0" role="button" title="Click to add"'}> 5821 + <span class="activity-chip ${isAttached ? 'activity-chip-attached' : ''}" data-id="${d.id}" ${isAttached ? '' : 'tabindex="0" role="button" title="click to add"'}> 5822 5822 <span class="activity-chip-icon">${d.icon || '📌'}</span> 5823 5823 <span>${escapeHtml(d.name)}</span> 5824 5824 </span> ··· 6359 6359 const previewDiv = document.getElementById('cleanupPreview'); 6360 6360 const previewText = document.getElementById('cleanupPreviewText'); 6361 6361 6362 - previewText.textContent = 'Scanning...'; 6362 + previewText.textContent = 'scanning...'; 6363 6363 previewDiv.style.display = ''; 6364 6364 6365 6365 try { ··· 6376 6376 if (result.error) throw new Error(result.error); 6377 6377 6378 6378 if (result.files_deleted === 0) { 6379 - previewText.textContent = 'No files to clean up.'; 6379 + previewText.textContent = 'no files to clean up.'; 6380 6380 document.getElementById('cleanupConfirmBtn').disabled = true; 6381 6381 } else { 6382 6382 previewText.textContent = result.files_deleted + ' files (' + result.bytes_freed_human ··· 6384 6384 document.getElementById('cleanupConfirmBtn').disabled = false; 6385 6385 } 6386 6386 } catch (err) { 6387 - previewText.textContent = 'Error: ' + err.message; 6387 + previewText.textContent = 'error: ' + err.message; 6388 6388 document.getElementById('cleanupConfirmBtn').disabled = true; 6389 6389 } 6390 6390 } ··· 6399 6399 const streamFilter = document.getElementById('cleanupStreamFilter').value; 6400 6400 const btn = document.getElementById('cleanupConfirmBtn'); 6401 6401 btn.disabled = true; 6402 - btn.textContent = 'Deleting...'; 6402 + btn.textContent = 'deleting...'; 6403 6403 6404 6404 try { 6405 6405 const response = await fetch('api/storage/purge', { ··· 6421 6421 notifyError('Cleanup Failed', err.message); 6422 6422 } finally { 6423 6423 btn.disabled = false; 6424 - btn.textContent = 'Delete Files'; 6424 + btn.textContent = 'delete files'; 6425 6425 } 6426 6426 } 6427 6427
+2 -2
apps/skills/call.py
··· 232 232 _pattern_observation_key(pattern, observation) == target_key 233 233 for observation in existing 234 234 ): 235 - raise _PatternCommandError("already recorded", 0) 235 + raise _PatternCommandError("already observed", 0) 236 236 existing.append( 237 237 { 238 238 "day": day, ··· 251 251 _emit_pattern_result( 252 252 pattern, 253 253 json_output=json_output, 254 - text_message=f"recorded observation: {slug}", 254 + text_message=f"observation saved: {slug}", 255 255 ) 256 256 257 257
+5 -5
apps/sol/routes.py
··· 421 421 422 422 if not use_file: 423 423 for match in talents_dir.glob(f"*/{use_id}_active.jsonl"): 424 - return jsonify({"error": "Talent run is still in progress"}), 202 425 - return jsonify({"error": f"Talent run {use_id} not found"}), 404 424 + return jsonify({"error": "talent run is still in progress"}), 202 425 + return jsonify({"error": f"talent run {use_id} not found"}), 404 426 426 427 427 try: 428 428 from think.cortex_client import get_use_end_state ··· 431 431 lines = f.readlines() 432 432 433 433 if not lines: 434 - return jsonify({"error": f"Talent run {use_id} is malformed"}), 500 434 + return jsonify({"error": f"talent run {use_id} is malformed"}), 500 435 435 436 436 first_line = lines[0].strip() 437 437 if not first_line: 438 - return jsonify({"error": f"Talent run {use_id} is malformed"}), 500 438 + return jsonify({"error": f"talent run {use_id} is malformed"}), 500 439 439 440 440 request_event = json.loads(first_line) 441 441 if request_event.get("event") != "request": 442 - return jsonify({"error": f"Talent run {use_id} is malformed"}), 500 442 + return jsonify({"error": f"talent run {use_id} is malformed"}), 500 443 443 444 444 event_data = _parse_use_events(lines[1:], collect_events=True) 445 445
+24 -24
apps/sol/workspace.html
··· 1095 1095 </div> 1096 1096 1097 1097 <div class="agents-content"> 1098 - <h2 class="sr-only">Agent Runs</h2> 1098 + <h2 class="sr-only">agent runs</h2> 1099 1099 <!-- Loading state --> 1100 1100 <div id="loading-view" class="loading" role="status" aria-live="polite"> 1101 1101 <div class="spinner" aria-hidden="true"></div> 1102 - <div>Loading agents...</div> 1102 + <div>loading agents...</div> 1103 1103 </div> 1104 1104 1105 1105 <div id="agents-status" class="sr-only" role="status" aria-live="polite"></div> ··· 1112 1112 <div id="empty-state" class="empty-state" style="display: none;"> 1113 1113 <div class="empty-state-icon">🤖</div> 1114 1114 <div class="empty-state-text">No agent runs on this day</div> 1115 - <div class="empty-state-hint">Agent runs will appear here when they complete</div> 1115 + <div class="empty-state-hint">agent runs will appear here when they complete</div> 1116 1116 </div> 1117 1117 </div> 1118 1118 ··· 1138 1138 <th class="col-model">model</th> 1139 1139 <th class="col-provider">provider</th> 1140 1140 <th class="col-runtime">runtime</th> 1141 - <th class="col-activity" title="Thinking events">💭</th> 1142 - <th class="col-activity" title="Tool calls">🔧</th> 1143 - <th class="col-activity" title="Cost">💰</th> 1141 + <th class="col-activity" title="thinking events">💭</th> 1142 + <th class="col-activity" title="tool calls">🔧</th> 1143 + <th class="col-activity" title="cost">💰</th> 1144 1144 <th class="col-facet">facet</th> 1145 1145 <th class="col-output">output</th> 1146 1146 <th class="col-prompt">prompt</th> ··· 1153 1153 <!-- Run Detail View --> 1154 1154 <div id="run-detail-view" style="display: none;"> 1155 1155 <div class="run-detail-breadcrumb"> 1156 - <a class="crumb-link" id="crumb-agents" href="#" onclick="event.preventDefault(); showGridView()">Agents</a> 1156 + <a class="crumb-link" id="crumb-agents" href="#" onclick="event.preventDefault(); showGridView()">agents</a> 1157 1157 <span class="crumb-sep">/</span> 1158 - <a class="crumb-link" id="crumb-agent" href="#">Agent Name</a> 1158 + <a class="crumb-link" id="crumb-agent" href="#">agent name</a> 1159 1159 <span class="crumb-sep">/</span> 1160 1160 <span class="crumb-current" id="crumb-run">Run 09:41</span> 1161 1161 </div> 1162 1162 <div id="run-detail-header-mount"></div> 1163 1163 <div id="run-detail-tabs" class="rd-tabs" role="tablist" aria-label="run detail tabs"> 1164 - <button type="button" class="rd-tab active" data-tab="log" id="tab-log" role="tab" aria-selected="true" aria-controls="run-detail-log-pane" tabindex="0">Run Log</button> 1165 - <button type="button" class="rd-tab" data-tab="output" id="tab-output" role="tab" aria-selected="false" aria-controls="run-detail-output-pane" tabindex="-1" style="display: none;">Output</button> 1164 + <button type="button" class="rd-tab active" data-tab="log" id="tab-log" role="tab" aria-selected="true" aria-controls="run-detail-log-pane" tabindex="0">run log</button> 1165 + <button type="button" class="rd-tab" data-tab="output" id="tab-output" role="tab" aria-selected="false" aria-controls="run-detail-output-pane" tabindex="-1" style="display: none;">output</button> 1166 1166 </div> 1167 1167 <div id="run-detail-panel" class="rd-panel"> 1168 1168 <div id="run-detail-log-pane" class="rd-tab-pane active" role="tabpanel" aria-labelledby="tab-log"></div> ··· 1175 1175 <div id="preview-modal" class="modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="preview-modal-title"> 1176 1176 <div class="modal-content"> 1177 1177 <div class="modal-header"> 1178 - <h3 class="modal-title" id="preview-modal-title">Agent Prompt</h3> 1178 + <h3 class="modal-title" id="preview-modal-title">agent prompt</h3> 1179 1179 <button class="modal-close" onclick="hidePreview()">&times;</button> 1180 1180 </div> 1181 1181 <div class="modal-body"> ··· 1219 1219 1220 1220 const ownerEl = document.getElementById('identity-owner'); 1221 1221 if (identity.name) { 1222 - ownerEl.textContent = 'Owner: ' + identity.name; 1222 + ownerEl.textContent = 'owner: ' + identity.name; 1223 1223 } 1224 1224 1225 1225 const signalsEl = document.getElementById('identity-signals'); ··· 1401 1401 const label = d.slice(0, 4) + '-' + d.slice(4, 6) + '-' + d.slice(6); 1402 1402 return `<a href="${d}">${label}</a>`; 1403 1403 }).join(', '); 1404 - banner.innerHTML = `<div class="updated-banner-title">Days with pending reprocessing</div>${links}`; 1404 + banner.innerHTML = `<div class="updated-banner-title">days with pending reprocessing</div>${links}`; 1405 1405 banner.style.display = 'block'; 1406 1406 }) 1407 1407 .catch(err => { ··· 1410 1410 if (!banner.querySelector('.updated-banner-stale')) { 1411 1411 const stale = document.createElement('div'); 1412 1412 stale.className = 'updated-banner-stale'; 1413 - stale.textContent = 'Status check failed — reload to refresh.'; 1413 + stale.textContent = 'status check failed — reload to refresh.'; 1414 1414 banner.appendChild(stale); 1415 1415 } 1416 1416 return; ··· 1420 1420 banner.innerHTML = ''; 1421 1421 const unknown = document.createElement('div'); 1422 1422 unknown.className = 'updated-banner-unknown'; 1423 - unknown.textContent = 'Status check failed; unknown if pending.'; 1423 + unknown.textContent = 'status check failed; unknown if pending.'; 1424 1424 banner.appendChild(unknown); 1425 1425 banner.style.display = 'block'; 1426 1426 }); ··· 1455 1455 document.getElementById('loading-view').innerHTML = 1456 1456 '<div class="empty-state" aria-live="polite">' + 1457 1457 '<div class="empty-state-icon">⚠️</div>' + 1458 - '<div class="empty-state-text">Unable to load talents</div>' + 1459 - '<div class="empty-state-hint">The server may be temporarily unavailable. Check your connection and try again.</div>' + 1460 - '<button class="back-btn" onclick="loadTalents()" style="margin-top: 1rem;">Try again</button>' + 1458 + '<div class="empty-state-text">unable to load talents</div>' + 1459 + '<div class="empty-state-hint">the server may be temporarily unavailable. check your connection and try again.</div>' + 1460 + '<button class="back-btn" onclick="loadTalents()" style="margin-top: 1rem;">try again</button>' + 1461 1461 '</div>'; 1462 1462 } 1463 1463 } ··· 1907 1907 e.preventDefault() 1908 1908 showRunList(agentName) 1909 1909 }; 1910 - document.getElementById('crumb-run').textContent = 'Run ...'; 1910 + document.getElementById('crumb-run').textContent = 'run ...'; 1911 1911 1912 1912 document.getElementById('run-detail-header-mount').innerHTML = 1913 1913 '<div class="run-loading"><div class="spinner"></div><div>Loading run details...</div></div>'; ··· 1980 1980 } else { 1981 1981 const empty = document.createElement('div'); 1982 1982 empty.className = 'run-loading'; 1983 - empty.textContent = 'No log events recorded for this run'; 1983 + empty.textContent = 'no log events for this run'; 1984 1984 logPane.appendChild(empty); 1985 1985 } 1986 1986 ··· 2291 2291 if (incomplete) { 2292 2292 const note = document.createElement('div'); 2293 2293 note.className = 'tool-incomplete'; 2294 - note.textContent = 'Tool call did not complete'; 2294 + note.textContent = 'tool call did not complete'; 2295 2295 details.appendChild(note); 2296 2296 } 2297 2297 ··· 2349 2349 } else { 2350 2350 const empty = document.createElement('div'); 2351 2351 empty.className = 'finish-empty'; 2352 - empty.textContent = 'No output'; 2352 + empty.textContent = 'no output'; 2353 2353 block.appendChild(empty); 2354 2354 } 2355 2355 ··· 2447 2447 const content = document.getElementById('preview-modal-content'); 2448 2448 const title = document.getElementById('preview-modal-title'); 2449 2449 2450 - content.textContent = 'Loading...'; 2450 + content.textContent = 'loading...'; 2451 2451 modal.classList.add('show'); 2452 2452 2453 2453 // Focus the close button ··· 2484 2484 content.textContent = data.full_prompt; 2485 2485 } 2486 2486 } catch (error) { 2487 - content.textContent = 'Error loading prompt'; 2487 + content.textContent = 'error loading prompt'; 2488 2488 } 2489 2489 }; 2490 2490
+40 -40
apps/speakers/workspace.html
··· 1085 1085 <div class="spk-layout"> 1086 1086 <nav class="spk-segments" aria-label="segments"> 1087 1087 <h2 class="spk-segments-header"> 1088 - Segments 1088 + segments 1089 1089 </h2> 1090 1090 <div id="spkSegmentsStatus" class="spk-segments-status" aria-live="polite"></div> 1091 1091 <ul class="spk-segments-list" id="spkSegmentList" role="listbox"> 1092 - <li class="spk-empty">Loading...</li> 1092 + <li class="spk-empty">loading...</li> 1093 1093 </ul> 1094 1094 <div class="spk-load-more" id="spkLoadMore" style="display: none;"> 1095 - <button class="spk-load-more-btn" id="spkLoadMoreBtn">Load more segments</button> 1095 + <button class="spk-load-more-btn" id="spkLoadMoreBtn">load more segments</button> 1096 1096 </div> 1097 1097 </nav> 1098 1098 ··· 1154 1154 const msg = document.createElement('div'); 1155 1155 msg.className = 'spk-status spk-status-error'; 1156 1156 msg.setAttribute('role', 'alert'); 1157 - msg.innerHTML = 'Couldn\'t load this audio<button class="spk-status-dismiss" aria-label="Dismiss">&times;</button>'; 1157 + msg.innerHTML = 'couldn\'t load this audio<button class="spk-status-dismiss" aria-label="dismiss">&times;</button>'; 1158 1158 msg.querySelector('.spk-status-dismiss').addEventListener('click', (e) => { 1159 1159 e.target.closest('.spk-status-error').remove(); 1160 1160 }); ··· 1270 1270 const name = input.value.trim(); 1271 1271 1272 1272 if (!name) { 1273 - status.textContent = 'Please enter a name.'; 1273 + status.textContent = 'please enter a name.'; 1274 1274 return; 1275 1275 } 1276 1276 1277 1277 btn.disabled = true; 1278 1278 input.disabled = true; 1279 - status.textContent = 'Identifying...'; 1279 + status.textContent = 'identifying...'; 1280 1280 1281 1281 fetch('/app/speakers/api/discovery/identify', { 1282 1282 method: 'POST', ··· 1292 1292 return; 1293 1293 } 1294 1294 1295 - status.textContent = `Identified as ${data.entity_name} (${data.voiceprints_saved} voiceprints, ${data.segments_updated} segments updated)`; 1295 + status.textContent = `identified as ${data.entity_name} (${data.voiceprints_saved} voiceprints, ${data.segments_updated} segments updated)`; 1296 1296 card.style.opacity = '0.5'; 1297 1297 loadSegments(); 1298 1298 if (selectedSegment && selectedSource) { ··· 1309 1309 }, 3000); 1310 1310 }) 1311 1311 .catch(() => { 1312 - status.textContent = 'Failed to identify. Try again.'; 1312 + status.textContent = 'failed to identify. try again.'; 1313 1313 btn.disabled = false; 1314 1314 input.disabled = false; 1315 1315 }); ··· 1407 1407 `).join('')} 1408 1408 </div> 1409 1409 <div class="spk-owner-actions"> 1410 - <button class="spk-owner-btn spk-owner-btn-confirm" id="spkOwnerConfirm">Yes, that's me</button> 1411 - <button class="spk-owner-btn spk-owner-btn-reject" id="spkOwnerReject">No, try again</button> 1410 + <button class="spk-owner-btn spk-owner-btn-confirm" id="spkOwnerConfirm">yes, that's me</button> 1411 + <button class="spk-owner-btn spk-owner-btn-reject" id="spkOwnerReject">no, try again</button> 1412 1412 </div> 1413 1413 </div> 1414 1414 `; ··· 1474 1474 </div> 1475 1475 <div class="spk-owner-actions"> 1476 1476 <button class="spk-owner-btn spk-owner-btn-confirm" id="spkOwnerHelp"> 1477 - Help solstone learn faster 1477 + help solstone learn faster 1478 1478 </button> 1479 1479 </div> 1480 1480 <div class="spk-owner-toast" id="spkOwnerGuideToast"></div> 1481 1481 <details class="spk-owner-diagnostics"> 1482 - <summary class="spk-owner-diagnostics-summary">Why not yet?</summary> 1482 + <summary class="spk-owner-diagnostics-summary">why not yet?</summary> 1483 1483 <div class="spk-owner-diagnostics-body"> 1484 - <div class="spk-owner-diagnostics-line">Source: ${escapeHtml(data.source || 'auto')}</div> 1484 + <div class="spk-owner-diagnostics-line">source: ${escapeHtml(data.source || 'auto')}</div> 1485 1485 ${data.low_quality_reason ? ` 1486 1486 <div class="spk-owner-diagnostics-line"> 1487 - Gate: ${escapeHtml(data.low_quality_reason)} — observed ${escapeHtml(formatOwnerMetric(data.observed_value))}, threshold ${escapeHtml(formatOwnerMetric(data.threshold_value))} 1487 + gate: ${escapeHtml(data.low_quality_reason)} — observed ${escapeHtml(formatOwnerMetric(data.observed_value))}, threshold ${escapeHtml(formatOwnerMetric(data.threshold_value))} 1488 1488 </div> 1489 1489 ` : ''} 1490 1490 <div class="spk-owner-diagnostics-line">Manual tags: ${escapeHtml(String(data.manual_tags_count || 0))}</div> ··· 1570 1570 </div> 1571 1571 `).join('')} 1572 1572 <form class="spk-discovery-form"> 1573 - <input type="text" class="spk-discovery-input" aria-label="Speaker name" placeholder="Enter speaker name..." /> 1574 - <button class="spk-discovery-btn">Name</button> 1573 + <input type="text" class="spk-discovery-input" aria-label="speaker name" placeholder="enter speaker name..." /> 1574 + <button class="spk-discovery-btn">name</button> 1575 1575 </form> 1576 1576 <div class="spk-discovery-status"></div> 1577 1577 </div> ··· 1671 1671 }) + '</li>'; 1672 1672 const header = document.querySelector('.spk-segments-header'); 1673 1673 if (header) { 1674 - header.textContent = 'Segments'; 1674 + header.textContent = 'segments'; 1675 1675 } 1676 1676 return; 1677 1677 } ··· 1732 1732 const header = document.querySelector('.spk-segments-header'); 1733 1733 if (header) { 1734 1734 if (segmentTotal > 20) { 1735 - header.innerHTML = `Segments <span class="spk-segments-count">${segments.length} of ${segmentTotal}</span>`; 1735 + header.innerHTML = `segments <span class="spk-segments-count">${segments.length} of ${segmentTotal}</span>`; 1736 1736 } else { 1737 - header.textContent = 'Segments'; 1737 + header.textContent = 'segments'; 1738 1738 } 1739 1739 } 1740 1740 } ··· 1798 1798 html += '<div class="spk-audio-player" id="spkAudioContainer"></div>'; 1799 1799 1800 1800 if (!detailExpanded) { 1801 - html += '<button class="spk-expand-btn" id="spkExpandBtn">Show sentences and details</button>'; 1801 + html += '<button class="spk-expand-btn" id="spkExpandBtn">show sentences and details</button>'; 1802 1802 } 1803 1803 1804 1804 html += '<div id="spkReviewStatus" class="spk-review-status" aria-live="polite"></div>'; 1805 - html += '<div class="spk-sentences" id="spkSentences"><div class="spk-empty">Loading...</div></div>'; 1805 + html += '<div class="spk-sentences" id="spkSentences"><div class="spk-empty">loading...</div></div>'; 1806 1806 detailPanel.innerHTML = html; 1807 1807 1808 1808 if (!detailExpanded) { ··· 1946 1946 if (audio_file) { 1947 1947 audioContainer.innerHTML = ` 1948 1948 <div class="spk-player-controls"> 1949 - <button class="spk-skip-back" id="spkSkipBack" title="Back 5s">&#8634; 5s</button> 1949 + <button class="spk-skip-back" id="spkSkipBack" title="back 5s">&#8634; 5s</button> 1950 1950 <audio id="spkAudio" controls preload="metadata"> 1951 1951 <source src="${audio_file}" type="audio/flac"> 1952 1952 </audio> 1953 - <button class="spk-speed-toggle" id="spkSpeedToggle" title="Playback speed">1x</button> 1953 + <button class="spk-speed-toggle" id="spkSpeedToggle" title="playback speed">1x</button> 1954 1954 </div> 1955 1955 `; 1956 1956 audioPlayer = document.getElementById('spkAudio'); 1957 1957 setupAudioListeners(); 1958 1958 } else { 1959 - audioContainer.innerHTML = '<div class="spk-empty">No audio file available</div>'; 1959 + audioContainer.innerHTML = '<div class="spk-empty">no audio file available</div>'; 1960 1960 audioPlayer = null; 1961 1961 } 1962 1962 ··· 1994 1994 const nonOwnerTotal = seg?.attribution_non_owner_total || 0; 1995 1995 const nullCount = seg?.attribution_null || 0; 1996 1996 const showPrompt = seg?.speaker_count > 0 && nonOwnerTotal > 0 && (nullCount / nonOwnerTotal) > 0.5; 1997 - return showPrompt ? `<div class="spk-seeding-prompt">This meeting has ${seg.speaker_count} named speakers but I can't match voices yet. Assigning even a few sentences will help me learn their voices for future meetings.</div>` : ''; 1997 + return showPrompt ? `<div class="spk-seeding-prompt">this meeting has ${seg.speaker_count} named speakers but I can't match voices yet. assigning even a few sentences will help me learn their voices for future meetings.</div>` : ''; 1998 1998 })()} 1999 1999 ${!hasLabels ? '<div class="spk-status spk-status-info">speakers haven\'t been identified yet — assign a few sentences to help solstone learn their voices</div>' : ''} 2000 - ${sentences.length === 0 ? '<div class="spk-empty">No sentences match this filter</div>' : sentences.map(renderSentence).join('')} 2000 + ${sentences.length === 0 ? '<div class="spk-empty">no sentences match this filter</div>' : sentences.map(renderSentence).join('')} 2001 2001 `; 2002 2002 2003 2003 container.querySelectorAll('.spk-filter-btn').forEach(btn => { ··· 2023 2023 2024 2024 function countLabelForFilter(visible, total) { 2025 2025 if (currentFilter === 'all') { 2026 - return `Sentences (${total})`; 2026 + return `sentences (${total})`; 2027 2027 } 2028 - return `Sentences (${visible} of ${total})`; 2028 + return `sentences (${visible} of ${total})`; 2029 2029 } 2030 2030 2031 2031 function renderSentence(sentence) { ··· 2042 2042 <div class="spk-status-container" id="status-${sentence.id}" role="status" aria-live="polite"></div> 2043 2043 </div> 2044 2044 <div class="spk-sentence-actions"> 2045 - <button class="spk-btn spk-btn-icon spk-play-btn" aria-label="Play from here" title="Play from here">&#9654;</button> 2045 + <button class="spk-btn spk-btn-icon spk-play-btn" aria-label="play from here" title="play from here">&#9654;</button> 2046 2046 ${actionsHtml} 2047 2047 </div> 2048 2048 </div> ··· 2051 2051 2052 2052 function renderSpeakerDisplay(sentence) { 2053 2053 if (!sentence.speaker_name) { 2054 - return '<span class="spk-review-state">Unassigned</span>'; 2054 + return '<span class="spk-review-state">unassigned</span>'; 2055 2055 } 2056 2056 2057 2057 let dotClass = ''; ··· 2074 2074 html += `<span>${escapeHtml(sentence.speaker_name)}</span>`; 2075 2075 2076 2076 if (sentence.needs_review) { 2077 - html += '<span class="spk-review-state">Needs review</span>'; 2077 + html += '<span class="spk-review-state">needs review</span>'; 2078 2078 } else if (sentence.method) { 2079 2079 html += `<span class="spk-review-state">${escapeHtml(METHOD_DISPLAY[sentence.method] || sentence.method)}</span>`; 2080 2080 } ··· 2093 2093 2094 2094 if (sentence.speaker_entity_id) { 2095 2095 const correctSelect = ` 2096 - <select class="spk-select-small spk-correct-select" aria-label="Correct attribution" data-sentence-id="${sentence.id}"> 2097 - <option value="">Correct...</option> 2096 + <select class="spk-select-small spk-correct-select" aria-label="correct attribution" data-sentence-id="${sentence.id}"> 2097 + <option value="">correct...</option> 2098 2098 ${options} 2099 2099 </select> 2100 - <button class="spk-btn spk-btn-primary spk-apply-btn" data-sentence-id="${sentence.id}" data-action="correct" style="display:none" aria-label="Apply correction">&#10003;</button> 2100 + <button class="spk-btn spk-btn-primary spk-apply-btn" data-sentence-id="${sentence.id}" data-action="correct" style="display:none" aria-label="apply correction">&#10003;</button> 2101 2101 `; 2102 2102 2103 2103 if (sentence.confidence === 'medium') { 2104 2104 return ` 2105 - <button class="spk-btn spk-btn-primary spk-confirm-btn" data-sentence-id="${sentence.id}">Confirm &#10003;</button> 2105 + <button class="spk-btn spk-btn-primary spk-confirm-btn" data-sentence-id="${sentence.id}">confirm &#10003;</button> 2106 2106 ${correctSelect} 2107 2107 `; 2108 2108 } ··· 2111 2111 } 2112 2112 2113 2113 return ` 2114 - <select class="spk-select-small spk-assign-select" aria-label="Assign speaker" data-sentence-id="${sentence.id}"> 2115 - <option value="">Assign...</option> 2114 + <select class="spk-select-small spk-assign-select" aria-label="assign speaker" data-sentence-id="${sentence.id}"> 2115 + <option value="">assign...</option> 2116 2116 ${options} 2117 2117 </select> 2118 - <button class="spk-btn spk-btn-primary spk-apply-btn" data-sentence-id="${sentence.id}" data-action="assign" style="display:none" aria-label="Apply assignment">&#10003;</button> 2118 + <button class="spk-btn spk-btn-primary spk-apply-btn" data-sentence-id="${sentence.id}" data-action="assign" style="display:none" aria-label="apply assignment">&#10003;</button> 2119 2119 `; 2120 2120 } 2121 2121 ··· 2298 2298 } 2299 2299 if (type === 'error') { 2300 2300 container.setAttribute('aria-live', 'assertive'); 2301 - container.innerHTML = `<div class="spk-status spk-status-${type}">${escapeHtml(message)}<button class="spk-status-dismiss" aria-label="Dismiss" onclick="this.closest('.spk-status-container').innerHTML='';this.closest('.spk-status-container').setAttribute('aria-live','polite')">&times;</button></div>`; 2301 + container.innerHTML = `<div class="spk-status spk-status-${type}">${escapeHtml(message)}<button class="spk-status-dismiss" aria-label="dismiss" onclick="this.closest('.spk-status-container').innerHTML='';this.closest('.spk-status-container').setAttribute('aria-live','polite')">&times;</button></div>`; 2302 2302 } else { 2303 2303 container.setAttribute('aria-live', 'polite'); 2304 2304 container.innerHTML = `<div class="spk-status spk-status-${type}">${escapeHtml(message)}</div>`; ··· 2349 2349 audioPlayer.addEventListener('error', () => { 2350 2350 const container = document.getElementById('spkAudioContainer'); 2351 2351 if (container) { 2352 - container.innerHTML = '<div class="spk-status spk-status-error" role="alert">Couldn\'t load audio — the file may be missing or in an unsupported format<button class="spk-status-dismiss" aria-label="Dismiss">&times;</button></div>'; 2352 + container.innerHTML = '<div class="spk-status spk-status-error" role="alert">couldn\'t load audio — the file may be missing or in an unsupported format<button class="spk-status-dismiss" aria-label="dismiss">&times;</button></div>'; 2353 2353 container.querySelector('.spk-status-dismiss')?.addEventListener('click', (e) => { 2354 2354 e.target.closest('.spk-status-error').remove(); 2355 2355 });
+16 -16
apps/stats/static/dashboard.js
··· 126 126 el('div', {className: 'empty-chart'}, [ 127 127 el('div', {style: 'font-size: 2em;'}, ['📊']), 128 128 el('div', {style: 'font-weight: 600; font-size: 1.1em;'}, ['No token data']), 129 - el('div', {style: 'color: #999;'}, ['No token usage recorded for this model']) 129 + el('div', {style: 'color: #999;'}, ['no token usage for this model']) 130 130 ]) 131 131 ); 132 132 return; ··· 229 229 const legend = el('div', {className: 'token-legend'}, [ 230 230 el('div', {className: 'legend-item'}, [ 231 231 el('div', {className: 'legend-color', style: {background: '#2171b5'}, 'aria-hidden': 'true'}), 232 - 'Input' 232 + 'input' 233 233 ]), 234 234 el('div', {className: 'legend-item'}, [ 235 235 el('div', { ··· 240 240 }, 241 241 'aria-hidden': 'true' 242 242 }), 243 - 'Reasoning' 243 + 'reasoning' 244 244 ]), 245 245 el('div', {className: 'legend-item'}, [ 246 246 el('div', { ··· 252 252 }, 253 253 'aria-hidden': 'true' 254 254 }), 255 - 'Output' 255 + 'output' 256 256 ]) 257 257 ]); 258 258 container.appendChild(legend); ··· 636 636 // Render stats cards 637 637 const statsGrid = document.getElementById('statsGrid'); 638 638 statsGrid.innerHTML = ''; // Clear existing content 639 - statsGrid.appendChild(statCard('total days', totalDays, 'days recorded')); 639 + statsGrid.appendChild(statCard('total days', totalDays, 'days')); 640 640 statsGrid.appendChild(statCard('audio hours', totalAudioHours, 'hours')); 641 641 statsGrid.appendChild(statCard('screen hours', totalScreenHours, 'hours')); 642 642 statsGrid.appendChild(statCard('total tokens', fmtTokens(totalTokens), 'tokens')); ··· 646 646 const progressSection = document.getElementById('progressSection'); 647 647 progressSection.innerHTML = ''; // Clear existing content 648 648 progressSection.appendChild( 649 - progressCard('Audio Processing', totals.transcript_sessions || 0, totals.pending_segments || 0) 649 + progressCard('audio processing', totals.transcript_sessions || 0, totals.pending_segments || 0) 650 650 ); 651 651 progressSection.appendChild( 652 - progressCard('Agent Outputs', totals.outputs_processed || 0, totals.outputs_pending || 0) 652 + progressCard('agent outputs', totals.outputs_processed || 0, totals.outputs_pending || 0) 653 653 ); 654 654 655 655 // Token usage setup ··· 661 661 if (models.length > 0) { 662 662 modelSelector.innerHTML = ''; 663 663 664 - // Add "Total" option first 665 - const totalOption = el('option', {value: 'total'}, ['Total']); 664 + // Add "total" option first 665 + const totalOption = el('option', {value: 'total'}, ['total']); 666 666 modelSelector.appendChild(totalOption); 667 667 668 668 // Add individual models ··· 714 714 stats.facets.counts_by_day || {}, 715 715 { 716 716 emptyIcon: '🏷️', 717 - emptyText: 'No facet data recorded', 718 - ariaLabel: 'Facets bar chart showing facet distribution over the last 30 days' 717 + emptyText: 'no facet data yet', 718 + ariaLabel: 'facets bar chart showing facet distribution over the last 30 days' 719 719 } 720 720 ); 721 721 ··· 725 725 stats.talents.counts_by_day || {}, 726 726 { 727 727 emptyIcon: '⚡', 728 - emptyText: 'No activity data recorded', 729 - ariaLabel: 'Activities bar chart showing activity counts over the last 30 days' 728 + emptyText: 'no activity data yet', 729 + ariaLabel: 'activities bar chart showing activity counts over the last 30 days' 730 730 } 731 731 ); 732 732 ··· 737 737 if (hasRepairs) { 738 738 const repairSection = document.getElementById('repairSection'); 739 739 const alert = el('div', {className: 'chart-section alert-repair'}, [ 740 - el('h2', {}, ['Items Needing Processing']), 740 + el('h2', {}, ['items needing processing']), 741 741 el('div', {className: 'stats-grid', id: 'repairGrid'}) 742 742 ]); 743 743 ··· 773 773 window.location.reload(); 774 774 return; 775 775 } 776 - throw new Error('Failed to load data'); 776 + throw new Error('failed to load data'); 777 777 } 778 778 return response.json(); 779 779 }) ··· 784 784 document.getElementById('loading').style.display = 'none'; 785 785 document.getElementById('notice').appendChild( 786 786 el('div', {className: 'alert alert-error'}, [ 787 - 'Failed to load dashboard data: ' + error.message 787 + 'failed to load dashboard data: ' + error.message 788 788 ]) 789 789 ); 790 790 });
+10 -10
apps/support/workspace.html
··· 522 522 <div class="support-feedback-form"> 523 523 <form id="feedback-form" novalidate> 524 524 <label for="feedback-text">your feedback</label> 525 - <textarea id="feedback-text" placeholder="What's on your mind?" required></textarea> 525 + <textarea id="feedback-text" placeholder="what's on your mind?" required></textarea> 526 526 <div class="support-field-error" id="feedback-error">please write something before sending</div> 527 527 <div class="support-feedback-options"> 528 528 <label><input type="checkbox" id="feedback-anonymous"> Submit anonymously</label> ··· 713 713 const statusClass = 'support-status-' + (t.status || 'open').replace(/[^a-z-]/g, ''); 714 714 715 715 let html = ` 716 - <button class="support-detail-back" id="back-to-list">&larr; Back to tickets</button> 716 + <button class="support-detail-back" id="back-to-list">&larr; back to tickets</button> 717 717 <h2>${esc(t.subject || 'Untitled')} <span class="support-status ${statusClass}">${esc(t.status || 'open')}</span></h2> 718 718 <div class="support-ticket-meta">#${t.id} &middot; ${esc(t.product || '')} &middot; ${esc(t.severity || '')} &middot; ${timeAgo(t.created_at)}</div> 719 719 <div class="support-message" style="margin-top:1rem;"> ··· 745 745 html += `<div class="support-reply-form"> 746 746 <form id="reply-form" novalidate> 747 747 <label for="reply-text">your reply</label> 748 - <textarea id="reply-text" placeholder="Write a reply..." required></textarea> 748 + <textarea id="reply-text" placeholder="write a reply..." required></textarea> 749 749 <div class="support-field-error" id="reply-error">please write something before sending</div> 750 - <div class="support-drop-zone" id="attach-zone" role="button" tabindex="0" aria-label="Attach files — drop files here or click to browse"> 750 + <div class="support-drop-zone" id="attach-zone" role="button" tabindex="0" aria-label="attach files — drop files here or click to browse"> 751 751 <input type="file" id="attach-input" multiple accept=".png,.jpg,.jpeg,.gif,.webp,.svg,.pdf,.txt,.csv,.html,.md,.xml,.json"> 752 752 Drop files here or click to attach (max 10 MB each, up to 5 files) 753 753 </div> ··· 807 807 fileList.innerHTML = pendingFiles.map((f, i) => 808 808 `<div class="support-file-entry"> 809 809 <span>\u{1F4CE} ${esc(f.name)} (${formatSize(f.size)})</span> 810 - <button class="remove-file" data-idx="${i}">\u00D7</button> 810 + <button class="remove-file" data-idx="${i}">\u00d7</button> 811 811 </div>` 812 812 ).join(''); 813 813 fileList.querySelectorAll('.remove-file').forEach(btn => { ··· 892 892 pendingFiles = []; 893 893 renderFileList(); 894 894 } 895 - status.textContent = text ? 'Reply sent.' : 'Files uploaded.'; 895 + status.textContent = text ? 'reply sent.' : 'files uploaded.'; 896 896 status.className = 'support-status-msg success'; 897 897 document.getElementById('reply-text').value = ''; 898 898 setTimeout(() => openTicket(id), 500); ··· 925 925 await uploadPendingFiles(id); 926 926 pendingFiles = []; 927 927 renderFileList(); 928 - status.textContent = 'Files uploaded.'; 928 + status.textContent = 'files uploaded.'; 929 929 status.className = 'support-status-msg success'; 930 930 setTimeout(() => openTicket(id), 500); 931 931 } catch (e) { ··· 945 945 '<div class="support-empty-heading">Couldn\'t load ticket.</div>' + 946 946 serverMessage + 947 947 '<div class="support-empty-hint">Go back and select it again.</div>' + 948 - '<button class="support-empty-action" id="error-back-btn">Back to tickets</button>' + 948 + '<button class="support-empty-action" id="error-back-btn">back to tickets</button>' + 949 949 '</div>'; 950 950 window.logError(err, { context: 'support-open-ticket' }); 951 951 const errorBackBtn = document.getElementById('error-back-btn'); ··· 981 981 body: JSON.stringify({body: text, anonymous: anon}) 982 982 }); 983 983 if (resp.ok) { 984 - status.textContent = 'Thanks for your feedback!'; 984 + status.textContent = 'thanks for your feedback!'; 985 985 status.className = 'support-status-msg success'; 986 986 feedbackTextEl.value = ''; 987 987 } else { ··· 1037 1037 const stale = document.createElement('div'); 1038 1038 stale.id = 'support-announcements-stale'; 1039 1039 stale.className = 'support-announcements-stale'; 1040 - stale.textContent = 'Couldn\'t refresh announcements - showing last known state.' + 1040 + stale.textContent = 'couldn\'t refresh announcements - showing last known state.' + 1041 1041 (announcementsLastSuccessAt ? ' Last updated ' + announcementsLastSuccessAt.toLocaleString() + '.' : ''); 1042 1042 banner.insertAdjacentElement('afterend', stale); 1043 1043 }
+18 -18
apps/todos/workspace.html
··· 520 520 {% for category, message in messages %} 521 521 <div class="flash-message {{ category }}" role="{% if category == 'error' %}alert{% else %}status{% endif %}"> 522 522 <span>{{ message }}</span> 523 - <button class="flash-close" aria-label="Dismiss message" onclick="this.parentElement.remove()">×</button> 523 + <button class="flash-close" aria-label="dismiss message" onclick="this.parentElement.remove()">×</button> 524 524 </div> 525 525 {% endfor %} 526 526 </div> ··· 555 555 class="facet-generate-btn" 556 556 data-facet="{{ facet_name }}" 557 557 data-day="{{ day }}" 558 - title="Generate weekly todos with AI" 559 - aria-label="Generate weekly todos with AI">✨</button> 558 + title="generate weekly todos with AI" 559 + aria-label="generate weekly todos with AI">✨</button> 560 560 <span class="facet-count">{{ count_emoji }}</span> 561 561 </div> 562 562 ··· 597 597 <button 598 598 type="button" 599 599 class="todo-action-btn edit" 600 - aria-label="Edit todo" 601 - title="Edit" 600 + aria-label="edit todo" 601 + title="edit" 602 602 data-index="{{ item.index }}" 603 603 data-facet="{{ facet_name }}" 604 604 > ··· 607 607 <button 608 608 type="button" 609 609 class="todo-action-btn move" 610 - aria-label="Move to another day" 611 - title="Move to another day" 610 + aria-label="move to another day" 611 + title="move to another day" 612 612 data-index="{{ item.index }}" 613 613 data-facet="{{ facet_name }}" 614 614 data-current-day="{{ day }}" ··· 619 619 <button 620 620 type="button" 621 621 class="todo-action-btn cancel" 622 - aria-label="Cancel todo" 623 - title="Cancel" 622 + aria-label="cancel todo" 623 + title="cancel" 624 624 data-index="{{ item.index }}" 625 625 data-facet="{{ facet_name }}" 626 626 > ··· 632 632 </ul> 633 633 <form class="todo-add-form" method="post" data-facet="{{ facet_name }}"> 634 634 <input type="hidden" name="action" value="add"> 635 - <label for="todo-add-{{ facet_name }}" class="sr-only">Add a todo to {{ facet_title }}</label> 635 + <label for="todo-add-{{ facet_name }}" class="sr-only">add a todo to {{ facet_title }}</label> 636 636 <input type="text" id="todo-add-{{ facet_name }}" name="text" placeholder="add a todo..." autocomplete="off"> 637 637 </form> 638 638 {% if facet_todos | length == 0 %} ··· 652 652 <div class="no-todos-hint">Add a todo below, or use #facet to target a specific list</div> 653 653 <form class="todo-add-form" method="post" data-facet=""> 654 654 <input type="hidden" name="action" value="add"> 655 - <label for="todo-add-standalone" class="sr-only">Add a todo</label> 655 + <label for="todo-add-standalone" class="sr-only">add a todo</label> 656 656 <input type="text" id="todo-add-standalone" name="text" placeholder="add a todo... (use #work, #personal, etc.)" autocomplete="off"> 657 657 </form> 658 658 </section> ··· 720 720 workspaceContent?.classList.remove('single-facet-mode'); 721 721 sections.forEach(s => s.style.display = ''); 722 722 if (noTodosEl) { 723 - noTodosEl.querySelector('.no-todos-text').textContent = 'No todos for today'; 723 + noTodosEl.querySelector('.no-todos-text').textContent = 'no todos for today'; 724 724 } 725 725 } else { 726 726 // Single-facet mode - hide headers, disable auto-collapse ··· 1414 1414 <button 1415 1415 type="button" 1416 1416 class="todo-action-btn edit" 1417 - aria-label="Edit todo" 1418 - title="Edit" 1417 + aria-label="edit todo" 1418 + title="edit" 1419 1419 data-index="${todo.index}" 1420 1420 data-facet="${facetName}" 1421 1421 > ··· 1424 1424 <button 1425 1425 type="button" 1426 1426 class="todo-action-btn move" 1427 - aria-label="Move to another day" 1428 - title="Move to another day" 1427 + aria-label="move to another day" 1428 + title="move to another day" 1429 1429 data-index="${todo.index}" 1430 1430 data-facet="${facetName}" 1431 1431 data-current-day="${currentDay}" ··· 1436 1436 <button 1437 1437 type="button" 1438 1438 class="todo-action-btn cancel" 1439 - aria-label="Cancel todo" 1440 - title="Cancel" 1439 + aria-label="cancel todo" 1440 + title="cancel" 1441 1441 data-index="${todo.index}" 1442 1442 data-facet="${facetName}" 1443 1443 >
+54 -54
apps/tokens/workspace.html
··· 2 2 <div id="tokens-loading"> 3 3 <div class="surface-state surface-state--loading" role="status" aria-busy="true"> 4 4 <div class="surface-state-spinner" aria-hidden="true"></div> 5 - <span class="surface-state-text" data-role="loading-status">Loading token usage data...</span> 5 + <span class="surface-state-text" data-role="loading-status">loading token usage data...</span> 6 6 </div> 7 7 </div> 8 8 9 9 <div class="dashboard-content" id="dashboard" style="display: none;"> 10 10 <!-- Daily Summary Card --> 11 - <section class="summary-card" aria-label="Daily summary"> 11 + <section class="summary-card" aria-label="daily summary"> 12 12 <table class="summary-table"> 13 13 <thead> 14 14 <tr> 15 - <th scope="col" aria-label="Category"></th> 16 - <th scope="col">Cost</th> 17 - <th scope="col">Tokens</th> 18 - <th scope="col">Requests</th> 19 - <th scope="col">Avg / Segment</th> 15 + <th scope="col" aria-label="category"></th> 16 + <th scope="col">cost</th> 17 + <th scope="col">tokens</th> 18 + <th scope="col">requests</th> 19 + <th scope="col">avg / segment</th> 20 20 </tr> 21 21 </thead> 22 22 <tbody> 23 23 <tr> 24 - <td class="row-label" title="Generate">Processing</td> 24 + <td class="row-label" title="generate">processing</td> 25 25 <td id="generate-cost">-</td> 26 26 <td id="generate-tokens">-</td> 27 27 <td id="generate-requests">-</td> 28 28 <td id="generate-seg-avg">-</td> 29 29 </tr> 30 30 <tr> 31 - <td class="row-label" title="Cogitate">Analysis</td> 31 + <td class="row-label" title="cogitate">analysis</td> 32 32 <td id="cogitate-cost">-</td> 33 33 <td id="cogitate-tokens">-</td> 34 34 <td id="cogitate-requests">-</td> ··· 42 42 43 43 <!-- By Provider --> 44 44 <section class="breakdown-section" aria-labelledby="section-provider"> 45 - <h2 id="section-provider">By Provider</h2> 45 + <h2 id="section-provider">by provider</h2> 46 46 <table class="breakdown-table" id="provider-table"> 47 47 <thead> 48 48 <tr> 49 - <th scope="col" class="sortable" data-sort="provider" role="button" tabindex="0" aria-sort="none">Provider</th> 50 - <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">Requests</th> 51 - <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">Tokens</th> 52 - <th scope="col" class="sortable num" data-sort="input_cost" role="button" tabindex="0" aria-sort="none">Input Cost</th> 53 - <th scope="col" class="sortable num" data-sort="output_cost" role="button" tabindex="0" aria-sort="none">Output Cost</th> 54 - <th scope="col" class="sortable num" data-sort="cached_tokens" role="button" tabindex="0" aria-sort="none">Cached</th> 55 - <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">Total</th> 56 - <th scope="col" class="num">% of Day</th> 49 + <th scope="col" class="sortable" data-sort="provider" role="button" tabindex="0" aria-sort="none">provider</th> 50 + <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">requests</th> 51 + <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">tokens</th> 52 + <th scope="col" class="sortable num" data-sort="input_cost" role="button" tabindex="0" aria-sort="none">input cost</th> 53 + <th scope="col" class="sortable num" data-sort="output_cost" role="button" tabindex="0" aria-sort="none">output cost</th> 54 + <th scope="col" class="sortable num" data-sort="cached_tokens" role="button" tabindex="0" aria-sort="none">cached</th> 55 + <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">total</th> 56 + <th scope="col" class="num">% of day</th> 57 57 </tr> 58 58 </thead> 59 59 <tbody id="provider-body"> 60 60 <tr class="empty-row"> 61 - <td colspan="8">No data for this day</td> 61 + <td colspan="8">no data for this day</td> 62 62 </tr> 63 63 </tbody> 64 64 </table> ··· 66 66 67 67 <!-- By Model --> 68 68 <section class="breakdown-section" aria-labelledby="section-model"> 69 - <h2 id="section-model">By Model</h2> 69 + <h2 id="section-model">by model</h2> 70 70 <table class="breakdown-table" id="model-table"> 71 71 <thead> 72 72 <tr> 73 - <th scope="col" class="sortable" data-sort="model" role="button" tabindex="0" aria-sort="none">Model</th> 74 - <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">Requests</th> 75 - <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">Tokens</th> 76 - <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">Total Cost</th> 77 - <th scope="col" class="num">% of Day</th> 78 - <th scope="col" class="sortable num" data-sort="avg_cost_per_request" role="button" tabindex="0" aria-sort="none">Per-Request Avg</th> 73 + <th scope="col" class="sortable" data-sort="model" role="button" tabindex="0" aria-sort="none">model</th> 74 + <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">requests</th> 75 + <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">tokens</th> 76 + <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">total cost</th> 77 + <th scope="col" class="num">% of day</th> 78 + <th scope="col" class="sortable num" data-sort="avg_cost_per_request" role="button" tabindex="0" aria-sort="none">per-request avg</th> 79 79 </tr> 80 80 </thead> 81 81 <tbody id="model-body"> 82 82 <tr class="empty-row"> 83 - <td colspan="6">No data for this day</td> 83 + <td colspan="6">no data for this day</td> 84 84 </tr> 85 85 </tbody> 86 86 </table> ··· 88 88 89 89 <!-- By Token Type --> 90 90 <section class="breakdown-section" aria-labelledby="section-token-type"> 91 - <h2 id="section-token-type">By Token Type</h2> 91 + <h2 id="section-token-type">by token type</h2> 92 92 <table class="breakdown-table" id="token-type-table"> 93 93 <thead> 94 94 <tr> 95 - <th scope="col">Token Type</th> 96 - <th scope="col" class="num">Total Tokens</th> 97 - <th scope="col" class="num">Cost</th> 98 - <th scope="col" class="num">% of Total Cost</th> 99 - <th scope="col" class="num">Avg Rate</th> 95 + <th scope="col">token type</th> 96 + <th scope="col" class="num">total tokens</th> 97 + <th scope="col" class="num">cost</th> 98 + <th scope="col" class="num">% of total cost</th> 99 + <th scope="col" class="num">avg rate</th> 100 100 </tr> 101 101 </thead> 102 102 <tbody id="token-type-body"> 103 103 <tr class="empty-row"> 104 - <td colspan="5">No data for this day</td> 104 + <td colspan="5">no data for this day</td> 105 105 </tr> 106 106 </tbody> 107 107 </table> ··· 109 109 110 110 <!-- By Context Prefix --> 111 111 <section class="breakdown-section" aria-labelledby="section-context"> 112 - <h2 id="section-context">By Context Prefix</h2> 112 + <h2 id="section-context">by context prefix</h2> 113 113 <div class="search-box"> 114 - <label for="context-search" class="sr-only">Filter context prefixes</label> 115 - <input type="text" id="context-search" placeholder="Filter contexts..." /> 114 + <label for="context-search" class="sr-only">filter context prefixes</label> 115 + <input type="text" id="context-search" placeholder="filter contexts..." /> 116 116 </div> 117 117 <table class="breakdown-table" id="context-table"> 118 118 <thead> 119 119 <tr> 120 - <th scope="col" class="sortable" data-sort="context" role="button" tabindex="0" aria-sort="none">Context Prefix</th> 121 - <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">Requests</th> 122 - <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">Tokens</th> 123 - <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">Total Cost</th> 124 - <th scope="col" class="num">% of Day</th> 125 - <th scope="col">Models Used</th> 120 + <th scope="col" class="sortable" data-sort="context" role="button" tabindex="0" aria-sort="none">context prefix</th> 121 + <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">requests</th> 122 + <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">tokens</th> 123 + <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">total cost</th> 124 + <th scope="col" class="num">% of day</th> 125 + <th scope="col">models used</th> 126 126 </tr> 127 127 </thead> 128 128 <tbody id="context-body"> 129 129 <tr class="empty-row"> 130 - <td colspan="6">No data for this day</td> 130 + <td colspan="6">no data for this day</td> 131 131 </tr> 132 132 </tbody> 133 133 </table> ··· 135 135 136 136 <!-- By Segment --> 137 137 <section class="breakdown-section" aria-labelledby="section-segment"> 138 - <h2 id="section-segment">By Segment</h2> 138 + <h2 id="section-segment">by segment</h2> 139 139 <div class="search-box"> 140 - <label for="segment-search" class="sr-only">Filter segments</label> 141 - <input type="text" id="segment-search" placeholder="Filter segments..." /> 140 + <label for="segment-search" class="sr-only">filter segments</label> 141 + <input type="text" id="segment-search" placeholder="filter segments..." /> 142 142 </div> 143 143 <table class="breakdown-table" id="segment-table"> 144 144 <thead> 145 145 <tr> 146 - <th scope="col" class="sortable" data-sort="segment" role="button" tabindex="0" aria-sort="none">Segment</th> 147 - <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">Requests</th> 148 - <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">Tokens</th> 149 - <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">Total Cost</th> 150 - <th scope="col" class="num">% of Day</th> 151 - <th scope="col">Models Used</th> 146 + <th scope="col" class="sortable" data-sort="segment" role="button" tabindex="0" aria-sort="none">segment</th> 147 + <th scope="col" class="sortable num" data-sort="requests" role="button" tabindex="0" aria-sort="none">requests</th> 148 + <th scope="col" class="sortable num" data-sort="tokens" role="button" tabindex="0" aria-sort="none">tokens</th> 149 + <th scope="col" class="sortable num" data-sort="cost" role="button" tabindex="0" aria-sort="none">total cost</th> 150 + <th scope="col" class="num">% of day</th> 151 + <th scope="col">models used</th> 152 152 </tr> 153 153 </thead> 154 154 <tbody id="segment-body"> 155 155 <tr class="empty-row"> 156 - <td colspan="6">No segment data for this day</td> 156 + <td colspan="6">no segment data for this day</td> 157 157 </tr> 158 158 </tbody> 159 159 </table>
+18 -18
apps/transcripts/workspace.html
··· 1385 1385 <div class="tr-wrap"> 1386 1386 <div class="tr-card"> 1387 1387 <!-- Left timeline --> 1388 - <div id="trTimeline" class="tr-timeline" aria-label="Day timeline"> 1388 + <div id="trTimeline" class="tr-timeline" aria-label="day timeline"> 1389 1389 <div class="tr-timeline-label">day</div> 1390 1390 <div class="tr-grid" id="trGrid"></div> 1391 1391 <div class="tr-labels" id="trLabels"></div> 1392 1392 <div class="tr-segments" id="trSegments"></div> 1393 1393 <div class="tr-sel-wrap" id="trSelWrap"> 1394 1394 <div class="tr-sel" data-handle="move"> 1395 - <div class="tr-bumper tr-bumper-top" data-handle="start" title="Drag to adjust start"></div> 1396 - <div class="tr-bumper tr-bumper-bottom" data-handle="end" title="Drag to adjust end"></div> 1395 + <div class="tr-bumper tr-bumper-top" data-handle="start" title="drag to adjust start"></div> 1396 + <div class="tr-bumper tr-bumper-bottom" data-handle="end" title="drag to adjust end"></div> 1397 1397 </div> 1398 1398 </div> 1399 1399 <div class="tr-timeline-legend"> ··· 1414 1414 <div class="tr-content"> 1415 1415 <div class="tr-header"> 1416 1416 <div> 1417 - <h2 class="tr-title">Transcripts</h2> 1417 + <h2 class="tr-title">transcripts</h2> 1418 1418 <div class="tr-range-text" id="trRangeText"></div> 1419 1419 <span class="tr-nav-hint" id="trNavHint">[ ] to navigate</span> 1420 1420 </div> 1421 - <button type="button" id="trDeleteBtn" class="tr-delete-btn" title="Delete segment"> 1421 + <button type="button" id="trDeleteBtn" class="tr-delete-btn" title="delete segment"> 1422 1422 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 1423 1423 <polyline points="3 6 5 6 21 6"></polyline> 1424 1424 <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path> ··· 1444 1444 <div id="trDeleteSegmentModal" class="modal" style="display: none;" role="dialog" aria-modal="true" aria-labelledby="trDeleteSegmentModalTitle"> 1445 1445 <div class="modal-content"> 1446 1446 <div class="modal-header danger"> 1447 - <h3 id="trDeleteSegmentModalTitle">Delete segment?</h3> 1447 + <h3 id="trDeleteSegmentModalTitle">delete segment?</h3> 1448 1448 <span class="close" id="trDeleteSegmentModalClose" onclick="closeDeleteSegmentModal()">&times;</span> 1449 1449 </div> 1450 1450 <div class="modal-body"> 1451 1451 <div id="trDeleteSegmentModalBody" class="delete-warning"></div> 1452 1452 </div> 1453 1453 <div class="modal-footer"> 1454 - <button type="button" class="btn-secondary" onclick="closeDeleteSegmentModal()">Cancel</button> 1455 - <button type="button" id="trDeleteSegmentModalConfirm" class="btn-danger" onclick="confirmDeleteSegment()">Delete</button> 1454 + <button type="button" class="btn-secondary" onclick="closeDeleteSegmentModal()">cancel</button> 1455 + <button type="button" id="trDeleteSegmentModalConfirm" class="btn-danger" onclick="confirmDeleteSegment()">delete</button> 1456 1456 </div> 1457 1457 </div> 1458 1458 </div> ··· 2365 2365 const statusEl = targetEl.querySelector('[data-role="loading-status"]'); 2366 2366 if (!statusEl) return; 2367 2367 if (!totalFrames) { 2368 - statusEl.textContent = 'Loading screen entries...'; 2368 + statusEl.textContent = 'loading screen entries...'; 2369 2369 return; 2370 2370 } 2371 2371 const decoded = Array.from(perVideoProgress.values()).reduce((sum, count) => sum + count, 0); ··· 2562 2562 const displayItems = textOnlyScreen ? chunks : groupBasicScreenFrames(chunks); 2563 2563 groupEntriesByIdx = new Map(); 2564 2564 2565 - let html = `<div class="tr-unified" role="list" aria-label="Transcript entries, ${displayItems.length} items">`; 2565 + let html = `<div class="tr-unified" role="list" aria-label="transcript entries, ${displayItems.length} items">`; 2566 2566 2567 2567 // Audio player section (if we have audio) 2568 2568 if (data.audio_file && showAudio) { ··· 2585 2585 html += renderScreenGroup(item, idx); 2586 2586 } else if (item.type === 'audio') { 2587 2587 const timeStr = item.time || ''; 2588 - html += `<div class="tr-entry tr-entry-audio" data-idx="${idx}" data-type="audio" data-timestamp="${item.timestamp}" role="listitem" tabindex="0" aria-label="Play from ${timeStr}">`; 2588 + html += `<div class="tr-entry tr-entry-audio" data-idx="${idx}" data-type="audio" data-timestamp="${item.timestamp}" role="listitem" tabindex="0" aria-label="play from ${timeStr}">`; 2589 2589 html += `<div class="tr-entry-time">${timeStr}</div>`; 2590 2590 html += '<div class="tr-entry-content">'; 2591 2591 html += '<span class="sr-only">Audio: </span>'; ··· 2595 2595 const labelClass = sl.is_owner ? 'tr-speaker-label tr-speaker-label-owner' : 'tr-speaker-label'; 2596 2596 const displayName = sl.is_owner ? 'You' : escapeHtml(sl.name); 2597 2597 const entityHref = '/app/entities#' + encodeURIComponent(sl.entity_id); 2598 - html += `<div class="${labelClass}" aria-label="Speaker: ${displayName}, ${sl.confidence} confidence"><span class="tr-speaker-dot ${dotClass}"></span><a href="${entityHref}">${displayName}</a><span class="sr-only">${sl.confidence} confidence</span></div>`; 2598 + html += `<div class="${labelClass}" aria-label="speaker: ${displayName}, ${sl.confidence} confidence"><span class="tr-speaker-dot ${dotClass}"></span><a href="${entityHref}">${displayName}</a><span class="sr-only">${sl.confidence} confidence</span></div>`; 2599 2599 } 2600 2600 html += `<div class="tr-entry-text">${escapeHtml(item.markdown)}</div>`; 2601 2601 html += '</div></div>'; ··· 2964 2964 const hasNext = currentFrameIndex < allScreenFrames.length - 1; 2965 2965 2966 2966 modal.innerHTML = ` 2967 - <div class="tr-modal-nav${hasPrev ? '' : ' disabled'}" data-dir="prev" title="Previous frame (Left arrow)" role="button" tabindex="${hasPrev ? '0' : '-1'}"> 2967 + <div class="tr-modal-nav${hasPrev ? '' : ' disabled'}" data-dir="prev" title="previous frame (left arrow)" role="button" tabindex="${hasPrev ? '0' : '-1'}"> 2968 2968 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg> 2969 2969 </div> 2970 2970 <div class="tr-modal-center"> 2971 2971 <div class="tr-modal-header"> 2972 2972 ${monitorPos ? `<span class="tr-modal-badge tr-modal-badge-monitor">${monitorPos}</span>` : ''} 2973 2973 ${category ? `<span class="tr-modal-badge tr-modal-badge-category">${escapeHtml(category)}</span>` : ''} 2974 - ${isMasked ? '<span class="tr-modal-badge tr-modal-badge-masked" title="Click image to reveal">Masked</span>' : ''} 2975 - <button class="tr-modal-close" title="Close (Esc)" aria-label="Close">&times;</button> 2974 + ${isMasked ? '<span class="tr-modal-badge tr-modal-badge-masked" title="click image to reveal">masked</span>' : ''} 2975 + <button class="tr-modal-close" title="close (esc)" aria-label="close">&times;</button> 2976 2976 </div> 2977 2977 <div class="tr-modal-img-wrap"> 2978 2978 <canvas id="trModalCanvas" class="loading${isMasked ? ' tr-masked-canvas' : ''}"></canvas> 2979 2979 </div> 2980 2980 ${description ? `<div class="tr-modal-description">${escapeHtml(description)}</div>` : ''} 2981 2981 </div> 2982 - <div class="tr-modal-nav${hasNext ? '' : ' disabled'}" data-dir="next" title="Next frame (Right arrow)" role="button" tabindex="${hasNext ? '0' : '-1'}"> 2982 + <div class="tr-modal-nav${hasNext ? '' : ' disabled'}" data-dir="next" title="next frame (right arrow)" role="button" tabindex="${hasNext ? '0' : '-1'}"> 2983 2983 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg> 2984 2984 </div> 2985 2985 `; ··· 3169 3169 panel.innerHTML = window.SurfaceState.empty({ 3170 3170 icon: emptyIcons.nothing, 3171 3171 heading: 'nothing here', 3172 - desc: 'no recordings were found for this day' 3172 + desc: 'no observations were found for this day' 3173 3173 }); 3174 3174 } 3175 3175 ··· 3534 3534 history.replaceState(null, '', window.location.pathname); 3535 3535 3536 3536 // Reset UI 3537 - titleEl.textContent = 'Transcripts'; 3537 + titleEl.textContent = 'transcripts'; 3538 3538 rangeText.textContent = ''; 3539 3539 tabsContainer.innerHTML = ''; 3540 3540 tabsContainer.classList.remove('visible');
+1 -1
convey/apps.py
··· 223 223 if day_count >= 7: 224 224 return "Ask me about your day, search your journal, or explore insights..." 225 225 return "Your daily analysis is ready — ask about today or anything in your journal..." 226 - return "Capture is running — your first daily analysis will be ready soon..." 226 + return "observing — your first daily analysis will be ready soon..." 227 227 228 228 229 229 def register_app_context(app: Flask, registry: AppRegistry) -> None:
+6 -6
convey/templates/app.html
··· 432 432 433 433 function renderTalentView(data) { 434 434 if (!modalTitle || !modalStatus || !modalTimeline) return; 435 - modalTitle.textContent = data.task || 'Talent run'; 435 + modalTitle.textContent = data.task || 'talent run'; 436 436 modalTitle.title = data.task || ''; 437 437 modalStatus.textContent = data.status || ''; 438 438 modalStatus.dataset.status = data.status || ''; ··· 441 441 if (!Array.isArray(data.events) || !data.events.length) { 442 442 var empty = document.createElement('p'); 443 443 empty.className = 'talent-view-empty'; 444 - empty.textContent = 'No events yet.'; 444 + empty.textContent = 'no events yet.'; 445 445 modalTimeline.appendChild(empty); 446 446 return; 447 447 } ··· 453 453 454 454 function renderTalentViewError(message) { 455 455 if (!modalTitle || !modalStatus || !modalTimeline) return; 456 - modalTitle.textContent = 'Talent run'; 456 + modalTitle.textContent = 'talent run'; 457 457 modalStatus.textContent = 'errored'; 458 458 modalStatus.dataset.status = 'errored'; 459 459 modalTimeline.innerHTML = ''; ··· 474 474 } catch (_err) { 475 475 // Fall back to generic talent-run copy when the error body is not JSON. 476 476 } 477 - throw new Error(body.error || 'Unable to load talent run'); 477 + throw new Error(body.error || 'unable to load talent run'); 478 478 } 479 479 return response.json(); 480 480 } ··· 513 513 } 514 514 } catch (err) { 515 515 stopTalentLiveUpdates(); 516 - renderTalentViewError(err && err.message ? err.message : 'Unable to load talent run'); 516 + renderTalentViewError(err && err.message ? err.message : 'unable to load talent run'); 517 517 } 518 518 } 519 519 ··· 524 524 stopTalentLiveUpdates(); 525 525 modalStatus.textContent = ''; 526 526 modalStatus.dataset.status = ''; 527 - modalTitle.textContent = 'Loading...'; 527 + modalTitle.textContent = 'loading...'; 528 528 modalTimeline.innerHTML = ''; 529 529 showTalentView(); 530 530 refreshTalentView(modalUseId);
+1 -1
convey/templates/chat_bar.html
··· 11 11 placeholder="{{ chat_bar_placeholder }}" 12 12 aria-label="chat input" 13 13 ></textarea> 14 - <button id="chatBarSend" type="submit" class="chat-bar-send" aria-label="send">Send</button> 14 + <button id="chatBarSend" type="submit" class="chat-bar-send" aria-label="send">send</button> 15 15 </form> 16 16 </div>
+3 -3
convey/templates/date_nav.html
··· 3 3 {% if day %} 4 4 <div class="date-nav"> 5 5 <div class="date-nav-left"> 6 - <button class="date-nav-arrow" id="date-nav-prev" title="Previous (←)">‹</button> 6 + <button class="date-nav-arrow" id="date-nav-prev" title="previous (←)">‹</button> 7 7 </div> 8 - <button class="date-nav-label" id="date-nav-label" title="Open month picker" aria-label="open month picker">{{ day|format_date_short }}</button> 8 + <button class="date-nav-label" id="date-nav-label" title="open month picker" aria-label="open month picker">{{ day|format_date_short }}</button> 9 9 <div class="date-nav-right"> 10 10 <button class="date-nav-today" id="date-nav-today" title="jump to today (t)" aria-label="go to today">T</button> 11 - <button class="date-nav-arrow" id="date-nav-next" title="Next (→)">›</button> 11 + <button class="date-nav-arrow" id="date-nav-next" title="next (→)">›</button> 12 12 </div> 13 13 <div class="month-picker"></div> 14 14 </div>
+4 -4
convey/templates/pairing.html
··· 3 3 <head> 4 4 <meta charset="utf-8"> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 - <title>Pair a phone - solstone</title> 6 + <title>pair a phone - solstone</title> 7 7 <link rel="stylesheet" href="{{ url_for('root.static', filename='app.css') }}"> 8 8 <link rel="stylesheet" href="{{ url_for('root.static', filename='pairing.css') }}"> 9 9 </head> ··· 11 11 <main class="pairing-shell"> 12 12 <section class="pairing-panel"> 13 13 <p class="pairing-kicker">Phone pairing</p> 14 - <h1>Pair a phone</h1> 14 + <h1>pair a phone</h1> 15 15 <p class="pairing-intro"> 16 16 Generate a short-lived code to pair your phone with this solstone journal. 17 17 Paired phones show up below as listening devices, and you can remove them from here. ··· 19 19 20 20 <div class="pairing-actions"> 21 21 <button id="pairing-generate" class="pairing-button" type="button"> 22 - Generate pairing code 22 + generate pairing code 23 23 </button> 24 24 <p id="pairing-feedback" class="pairing-feedback" aria-live="polite"></p> 25 25 </div> ··· 54 54 <div class="pairing-panel-head"> 55 55 <div> 56 56 <p class="pairing-kicker">Listening devices</p> 57 - <h2>Paired phones</h2> 57 + <h2>paired phones</h2> 58 58 </div> 59 59 </div> 60 60
+2 -2
tests/test_app_reflections.py
··· 54 54 assert response.status_code == 200 55 55 assert "weekly reflection" in html 56 56 assert "week of Sunday March 8th" in html 57 - assert ">Copy<" in html 58 - assert ">Download PDF<" in html 57 + assert ">copy<" in html 58 + assert ">download PDF<" in html 59 59 60 60 61 61 def test_reflections_detail_canonicalizes_to_sunday(journal_copy):
+1 -1
tests/test_apps_skills_call.py
··· 290 290 ) 291 291 292 292 assert result.exit_code == 0 293 - assert "already recorded" in result.stderr 293 + assert "already observed" in result.stderr 294 294 rows = load_patterns() 295 295 assert len(rows[0]["observations"]) == 1 296 296
+2 -2
tests/test_convey_apps.py
··· 27 27 28 28 current = {"imports": {"has_imported": True}} 29 29 result = _resolve_placeholder(current, 0) 30 - assert "Capture is running" in result 30 + assert "observing" in result 31 31 32 32 def test_first_daily_young(self): 33 33 from convey.apps import _resolve_placeholder ··· 58 58 from convey.apps import _resolve_placeholder 59 59 60 60 result = _resolve_placeholder({}, 5) 61 - assert "Capture is running" in result 61 + assert "observing" in result 62 62 63 63 64 64 class TestAttentionResolution:
+1 -1
tests/test_pairing_ui.py
··· 24 24 25 25 assert response.status_code == 200 26 26 body = response.get_data(as_text=True) 27 - assert "Pair a phone" in body 27 + assert "pair a phone" in body 28 28 assert "/static/pairing-qr.js" in body 29 29 assert "/static/pairing.js" in body 30 30 assert "/static/pairing.css" in body