diff --git a/docs/CONVEY-FRONTEND.md b/docs/CONVEY-FRONTEND.md
index 26f2e15e9..e31c65f9e 100644
--- a/docs/CONVEY-FRONTEND.md
+++ b/docs/CONVEY-FRONTEND.md
@@ -67,7 +67,7 @@ them in HTML.
"facets": [{ "name": "work", "title": "Work", "color": "#4A90D9" }],
"selected_facet": "work",
"chat_bar": {
- "placeholder": "Send a message...",
+ "placeholder": "send a message…",
"attention": null,
"sol_request": null
},
diff --git a/solstone/convey/copy.py b/solstone/convey/copy.py
index 545569ca8..d66ee73fe 100644
--- a/solstone/convey/copy.py
+++ b/solstone/convey/copy.py
@@ -12,8 +12,8 @@ CONVEY_UNKNOWN_ERROR = (
CONVEY_LOG_READ_FAILED = "i couldn't read that log. reload to try again."
CONVEY_ACTION_TRY_AGAIN = "Try again" # generic load-failure retry button
CONVEY_ACTION_RELOAD = "Reload" # full-surface reload affordance
-CONVEY_ACTION_RECONNECT = "Reconnect" # transport/socket reconnection affordance
-CONVEY_ACTION_RESTART = "Restart" # service/observer restart affordance
+CONVEY_ACTION_RECONNECT = "reconnect" # transport/socket reconnection affordance
+CONVEY_ACTION_RESTART = "restart" # service/observer restart affordance
CONVEY_CONSOLE_HEADING = "system messages"
CONVEY_CONSOLE_EMPTY = "I haven't seen any system messages this session."
CONVEY_CONSOLE_TAB_ALL = "all"
@@ -21,11 +21,11 @@ CONVEY_CONSOLE_TAB_ERRORS = "errors"
CONVEY_CONSOLE_TAB_WARNINGS = "warnings"
CONVEY_CONSOLE_TAB_INFO = "info"
CONVEY_CONSOLE_LINK_LABEL = "system messages"
-CONVEY_CONSOLE_ACTION_SEND = "Send"
-CONVEY_CONSOLE_ACTION_SEND_ALL = "Send all"
-CONVEY_CONSOLE_ACTION_CLEAR = "Clear"
-CONVEY_CONSOLE_ACTION_SHOW_DETAILS = "Show details"
-CONVEY_CONSOLE_ACTION_CLOSE = "Close"
+CONVEY_CONSOLE_ACTION_SEND = "send"
+CONVEY_CONSOLE_ACTION_SEND_ALL = "send all"
+CONVEY_CONSOLE_ACTION_CLEAR = "clear"
+CONVEY_CONSOLE_ACTION_SHOW_DETAILS = "show details"
+CONVEY_CONSOLE_ACTION_CLOSE = "close"
CONVEY_CONSOLE_REPORTING_OFF = "I can show these messages, but sending reports is off."
CONVEY_CONSOLE_SNAPSHOT_HEADING = "snapshot of {count} system messages"
CONVEY_CONSOLE_REPORT_HEADING = "Last {count} console entries:"
@@ -42,7 +42,7 @@ CONVEY_REPORT_SEVERITY_LABEL = "severity"
CONVEY_REPORT_SEVERITY_LOW = "low"
CONVEY_REPORT_SEVERITY_MEDIUM = "medium"
CONVEY_REPORT_SEVERITY_HIGH = "high"
-CONVEY_REPORT_ANONYMOUS_LABEL = "Submit anonymously"
+CONVEY_REPORT_ANONYMOUS_LABEL = "submit anonymously"
CONVEY_REPORT_DIAGNOSTIC_SUMMARY = "diagnostic context"
CONVEY_REPORT_DIAGNOSTIC_FOOTER = (
"(System snapshot bundled server-side via support_create's auto_context=true path.)"
@@ -52,28 +52,28 @@ CONVEY_REPORT_DIAGNOSTIC_STATUS_TEXT_LABEL = "Status text"
CONVEY_REPORT_DIAGNOSTIC_REQUEST_URL_LABEL = "Request URL"
CONVEY_REPORT_DIAGNOSTIC_SERVER_REASON_LABEL = "Server reason"
CONVEY_REPORT_DIAGNOSTIC_CORRELATION_ID_LABEL = "Correlation ID"
-CONVEY_REPORT_DIAGNOSTIC_REASON_CODE_LABEL = "Reason code"
-CONVEY_REPORT_DIAGNOSTIC_TIME_LABEL = "Time"
+CONVEY_REPORT_DIAGNOSTIC_REASON_CODE_LABEL = "reason code"
+CONVEY_REPORT_DIAGNOSTIC_TIME_LABEL = "time"
CONVEY_REPORT_DIAGNOSTIC_RAW_DETAIL_LABEL = "Raw detail"
CONVEY_REPORT_DIAGNOSTIC_CONSOLE_HEADING = "Last 5 console entries:"
CONVEY_REPORT_NO_CONSOLE = "(no console entries captured yet)"
-CONVEY_REPORT_ACTION_SEND = "Send report"
-CONVEY_REPORT_ACTION_SENDING = "Sending…"
-CONVEY_REPORT_ACTION_CANCEL = "Cancel"
-CONVEY_REPORT_ACTION_CLOSE = "Close"
-CONVEY_REPORT_ACTION_OPEN_EMAIL = "Open email draft"
-CONVEY_REPORT_ACTION_VIEW_TICKET = "View ticket"
-CONVEY_REPORT_SUCCESS_BODY = "I sent your report — reference #{ticket_id}."
+CONVEY_REPORT_ACTION_SEND = "send report"
+CONVEY_REPORT_ACTION_SENDING = "sending…"
+CONVEY_REPORT_ACTION_CANCEL = "cancel"
+CONVEY_REPORT_ACTION_CLOSE = "close"
+CONVEY_REPORT_ACTION_OPEN_EMAIL = "open email draft"
+CONVEY_REPORT_ACTION_VIEW_TICKET = "view ticket"
+CONVEY_REPORT_SUCCESS_BODY = "i sent your report. reference #{ticket_id}."
CONVEY_REPORT_SUCCESS_BODY_NO_ID = "I sent your report."
CONVEY_REPORT_FALLBACK_HEADLINE = "I couldn't reach support"
-CONVEY_REPORT_FALLBACK_BODY = "here's your report — paste it in an email."
+CONVEY_REPORT_FALLBACK_BODY = "here's your report. paste it in an email."
CONVEY_REPORT_FAILURE_HTTP_PREFIX = "HTTP"
CONVEY_REPORT_FAILURE_REFERENCE_PREFIX = "reference"
CONVEY_REPORT_MAILTO_BODY_PREFIX = (
"I couldn't send this through convey, so here's the report.\n\n"
)
CONVEY_REPORT_MAILTO_TRUNCATION_SUFFIX = "\n\n[full report is in your clipboard]\n"
-CONVEY_REPORT_BUTTON_LABEL = "Report this"
+CONVEY_REPORT_BUTTON_LABEL = "report this"
OBSERVER_CALLOSUM_LIVE_LABEL = "live"
diff --git a/solstone/convey/static/app.js b/solstone/convey/static/app.js
index 218c24b85..d89d58be4 100644
--- a/solstone/convey/static/app.js
+++ b/solstone/convey/static/app.js
@@ -69,13 +69,13 @@
pill.style.background = '';
pill.style.color = '';
pill.style.borderColor = '';
- pill.title = 'Click to show all facets';
+ pill.title = 'show all facets';
} else {
pill.classList.remove('selected');
pill.style.background = '';
pill.style.color = '';
pill.style.borderColor = '';
- pill.title = `Click to filter by ${facet.title}`;
+ pill.title = `filter by ${facet.title}`;
}
pill.setAttribute('aria-pressed', String(isSelected));
pill.style.boxShadow = '';
@@ -250,7 +250,7 @@
const addButton = document.createElement('button');
addButton.className = 'facet-add-pill';
addButton.textContent = '+';
- addButton.title = 'Create new facet';
+ addButton.title = 'create new facet';
addButton.setAttribute('aria-label', 'add facet');
addButton.onclick = () => openFacetCreateModal();
facetPillsContainer.appendChild(addButton);
@@ -1364,7 +1364,7 @@
create new facet
@@ -1637,26 +1637,26 @@ window.SurfaceState = (() => {
const timestamp = formatDetailTimestamp(detail.timestamp);
if (timestamp) {
- lines.push(`Time: ${escapeHtml(timestamp)}
`);
+ lines.push(`time: ${escapeHtml(timestamp)}
`);
}
if (hasValue(detail.correlationId)) {
const correlationId = String(detail.correlationId);
lines.push(
- `Reference:
diff --git a/solstone/convey/static/shell_boot.js b/solstone/convey/static/shell_boot.js
index 86ccff128..2d56e0a3c 100644
--- a/solstone/convey/static/shell_boot.js
+++ b/solstone/convey/static/shell_boot.js
@@ -179,7 +179,7 @@
window.solChatBarAttention = chatBar.attention || null;
const input = document.getElementById('chatBarInput');
if (input) {
- input.placeholder = chatBar.placeholder || 'Send a message...';
+ input.placeholder = chatBar.placeholder || 'send a message…';
}
}
diff --git a/solstone/convey/static/status_pane.js b/solstone/convey/static/status_pane.js
index 7255fd7cb..1ee8c6b80 100644
--- a/solstone/convey/static/status_pane.js
+++ b/solstone/convey/static/status_pane.js
@@ -113,7 +113,7 @@
statusSentence.textContent = 'connecting…';
statusSentence.style.color = '#f59e0b';
} else {
- statusSentence.textContent = 'connection lost — reconnecting';
+ statusSentence.textContent = 'connection lost. reconnecting';
statusSentence.style.color = '#ef4444';
}
}
@@ -195,7 +195,7 @@
function restartObserverFromStatusPane(button, resultEl) {
button.disabled = true;
- button.textContent = 'reconnecting...';
+ button.textContent = 'reconnecting…';
resultEl.textContent = '';
// Observer rows are per registration key; supervisor restarts the shared sense worker.
window.apiJson('/app/health/api/restart-observer', {
@@ -331,7 +331,7 @@
section.style.display = '';
if (version?.update_available) {
text.textContent = '';
- text.appendChild(document.createTextNode('v' + (version.current || '?') + ' — '));
+ text.appendChild(document.createTextNode('v' + (version.current || '?') + ' · '));
const span = document.createElement('span');
span.style.color = '#f59e0b';
span.textContent = 'update available (v' + (version.latest || '?') + ')';
@@ -516,8 +516,8 @@
bell.setAttribute('aria-label', 'browser notifications enabled');
} else if (perm === 'denied') {
bell.textContent = '🔕';
- bell.title = 'notifications blocked — update in browser settings';
- bell.setAttribute('aria-label', 'notifications blocked — update in browser settings');
+ bell.title = 'notifications blocked. update in browser settings';
+ bell.setAttribute('aria-label', 'notifications blocked. update in browser settings');
} else {
bell.textContent = '🔔';
bell.title = 'enable browser notifications';
diff --git a/solstone/convey/static/tests/date-nav.html b/solstone/convey/static/tests/date-nav.html
index fc5a4b907..6c7ff87f0 100644
--- a/solstone/convey/static/tests/date-nav.html
+++ b/solstone/convey/static/tests/date-nav.html
@@ -101,11 +101,11 @@
window.DateNav.isSelectableFutureDay('20991231', { allow_future: false }, now),
false
);
- equal('week control label', window.DateNav.weekControlLabel('20260712', now), 'Week of Jul 12');
- equal('week control label other year', window.DateNav.weekControlLabel('20251228', now), "Week of Dec 28 '25");
+ equal('week control label', window.DateNav.weekControlLabel('20260712', now), 'week of Jul 12');
+ equal('week control label other year', window.DateNav.weekControlLabel('20251228', now), "week of Dec 28 '25");
equal('week heading this week', window.DateNav.weekHeadingLabel('20260712', now), 'This week');
equal('week heading last week', window.DateNav.weekHeadingLabel('20260705', now), 'Last week');
- equal('week heading older week', window.DateNav.weekHeadingLabel('20260628', now), 'Week of Jun 28');
+ equal('week heading older week', window.DateNav.weekHeadingLabel('20260628', now), 'week of Jun 28');
equal('stepDay day step', window.DateNav.stepDay('20260308', 1, {}), '20260309');
equal('stepDay week step', window.DateNav.stepDay('20260308', 1, { step: 'week' }), '20260315');
equal('sundayOf mid-week', window.DateNav.sundayOf('20260310'), '20260308');
@@ -204,7 +204,7 @@
}
);
equal('week mounted canonical day', window.DateNav.day(), '20260308');
- equal('week mounted control label', document.querySelector('[data-date-nav-label]')?.textContent, 'Week of Mar 8');
+ equal('week mounted control label', document.querySelector('[data-date-nav-label]')?.textContent, 'week of Mar 8');
document.querySelector('[data-date-nav-trigger]')?.click();
await tick();
await tick();
diff --git a/solstone/convey/static/tests/day-grid.html b/solstone/convey/static/tests/day-grid.html
index 81a1decc6..d1520c266 100644
--- a/solstone/convey/static/tests/day-grid.html
+++ b/solstone/convey/static/tests/day-grid.html
@@ -89,6 +89,10 @@
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
}
+ function tick() {
+ return new Promise((resolve) => setTimeout(resolve, 0));
+ }
+
function setPointer(coarse) {
window.matchMedia = (query) => ({
matches: query === '(pointer: coarse)' ? coarse : Boolean(nativeMatchMedia?.(query)?.matches),
@@ -493,6 +497,8 @@
mar4 = root.querySelector('[data-daygrid-date="20260304"]');
mar20 = root.querySelector('[data-daygrid-date="20260320"]');
clickObserved(selected.host, mar4);
+ await tick();
+ equal('select first click live prompt', root.querySelector('.daygrid-live')?.textContent, 'range starts Today. pick the last day.');
clickObserved(selected.host, mar20);
equal('select forward order range from', selected.ranges[0]?.from, '20260304');
equal('select forward order range to', selected.ranges[0]?.to, '20260320');
diff --git a/solstone/convey/static/tests/diagnostic-console.html b/solstone/convey/static/tests/diagnostic-console.html
index 63ed1b2d3..ea5928ea6 100644
--- a/solstone/convey/static/tests/diagnostic-console.html
+++ b/solstone/convey/static/tests/diagnostic-console.html
@@ -13,9 +13,9 @@
I can show these messages, but sending reports is off.
@@ -40,11 +40,11 @@
CONSOLE_TAB_WARNINGS: 'warnings',
CONSOLE_TAB_INFO: 'info',
CONSOLE_LINK_LABEL: 'system messages',
- CONSOLE_ACTION_SEND: 'Send',
- CONSOLE_ACTION_SEND_ALL: 'Send all',
- CONSOLE_ACTION_CLEAR: 'Clear',
- CONSOLE_ACTION_SHOW_DETAILS: 'Show details',
- CONSOLE_ACTION_CLOSE: 'Close',
+ CONSOLE_ACTION_SEND: 'send',
+ CONSOLE_ACTION_SEND_ALL: 'send all',
+ CONSOLE_ACTION_CLEAR: 'clear',
+ CONSOLE_ACTION_SHOW_DETAILS: 'show details',
+ CONSOLE_ACTION_CLOSE: 'close',
CONSOLE_REPORTING_OFF: 'I can show these messages, but sending reports is off.',
CONSOLE_SNAPSHOT_HEADING: 'snapshot of {count} system messages',
CONSOLE_REPORT_HEADING: 'Last {count} console entries:',
@@ -159,10 +159,10 @@
document.querySelector('[data-diagnostic-filter="all"]').click();
document.querySelector('[data-diagnostic-send]').click();
let lastReport = window.reportCalls.at(-1);
- assert('entry Send calls reportError with entries', lastReport.source === 'console-entry' && lastReport.consoleEntries.length === 2);
+ assert('entry send calls reportError with entries', lastReport.source === 'console-entry' && lastReport.consoleEntries.length === 2);
document.querySelector('[data-diagnostic-action="send-all"]').click();
lastReport = window.reportCalls.at(-1);
- assert('Send all calls reportError with visible entries', lastReport.source === 'manual' && lastReport.consoleEntries.length === 2);
+ assert('send all calls reportError with visible entries', lastReport.source === 'manual' && lastReport.consoleEntries.length === 2);
diagnostic.clear();
window.logError(new Error('strip check'), { context: 'smoke' });
diff --git a/solstone/convey/static/tests/surface-state.html b/solstone/convey/static/tests/surface-state.html
index b2e4ebee8..96b248467 100644
--- a/solstone/convey/static/tests/surface-state.html
+++ b/solstone/convey/static/tests/surface-state.html
@@ -22,7 +22,7 @@
window.CONVEY_COPY = {
RELOAD_HINT: 'reload to try again.',
REPORT_DEFAULT_SUBJECT: 'convey error',
- REPORT_BUTTON_LABEL: 'Report this'
+ REPORT_BUTTON_LABEL: 'report this'
};
window.CONVEY_SETTINGS = { reportingEnabled: true };
window.marked = { parse(value) { return value; } };
@@ -91,12 +91,12 @@
}
});
const detail = parse(detailHtml);
- assert('detail summary text', detail.querySelector('.surface-state-detail summary').textContent === 'Show details');
+ assert('detail summary text', detail.querySelector('.surface-state-detail summary').textContent === 'show details');
assert('detail renders HTTP line', detail.textContent.includes('HTTP 500 Server Error · /api/test'));
assert('detail escapes server reason', detailHtml.includes('Raw <detail>'));
assert('detail renders locale timestamp', detail.textContent.includes(new Date(detailTimestamp).toLocaleString()));
assert('detail renders reference button', !!detail.querySelector('.surface-state-copy-reference'));
- assert('detail renders reason code', detail.textContent.includes('Reason code: parse'));
+ assert('detail renders reason code', detail.textContent.includes('reason code: parse'));
let copied = '';
const clipboardSpy = { writeText: async value => { copied = value; } };
diff --git a/solstone/convey/static/websocket.js b/solstone/convey/static/websocket.js
index ca1536ca6..7e3d5583b 100644
--- a/solstone/convey/static/websocket.js
+++ b/solstone/convey/static/websocket.js
@@ -378,7 +378,7 @@
app: 'system',
icon: '⚠️',
title: 'connection lost',
- message: 'reconnecting — some features may be delayed',
+ message: 'reconnecting. some features may be delayed',
dismissible: false
});
if (id != null) {
diff --git a/solstone/convey/tests/test_status_pane_copy_js.py b/solstone/convey/tests/test_status_pane_copy_js.py
index 6eb1a6850..954c70314 100644
--- a/solstone/convey/tests/test_status_pane_copy_js.py
+++ b/solstone/convey/tests/test_status_pane_copy_js.py
@@ -77,7 +77,7 @@ global.document = {
}
};
global.window = {
- CONVEY_COPY: { ACTION_RECONNECT: 'Reconnect' },
+ CONVEY_COPY: { ACTION_RECONNECT: 'reconnect' },
apiJson() {
return Promise.resolve({});
}
diff --git a/tests/test_convey_copy_console_parity.py b/tests/test_convey_copy_console_parity.py
index 752af65ca..28d5863ef 100644
--- a/tests/test_convey_copy_console_parity.py
+++ b/tests/test_convey_copy_console_parity.py
@@ -27,6 +27,31 @@ def test_non_console_copy_matches_browser_mirror() -> None:
assert _js_constant(text, "LOG_READ_FAILED") == copy.CONVEY_LOG_READ_FAILED
+def test_manifest_touched_action_and_report_copy_matches_browser_mirror() -> None:
+ text = Path("solstone/convey/static/convey_copy.js").read_text(encoding="utf-8")
+
+ names = (
+ "CONVEY_ACTION_RECONNECT",
+ "CONVEY_ACTION_RESTART",
+ "CONVEY_REPORT_ANONYMOUS_LABEL",
+ "CONVEY_REPORT_DIAGNOSTIC_REASON_CODE_LABEL",
+ "CONVEY_REPORT_DIAGNOSTIC_TIME_LABEL",
+ "CONVEY_REPORT_ACTION_SEND",
+ "CONVEY_REPORT_ACTION_SENDING",
+ "CONVEY_REPORT_ACTION_CANCEL",
+ "CONVEY_REPORT_ACTION_CLOSE",
+ "CONVEY_REPORT_ACTION_OPEN_EMAIL",
+ "CONVEY_REPORT_ACTION_VIEW_TICKET",
+ "CONVEY_REPORT_SUCCESS_BODY",
+ "CONVEY_REPORT_FALLBACK_BODY",
+ "CONVEY_REPORT_BUTTON_LABEL",
+ )
+
+ for python_name in names:
+ js_name = python_name.removeprefix("CONVEY_")
+ assert _js_constant(text, js_name) == getattr(copy, python_name)
+
+
def _js_constant(text: str, name: str) -> str:
match = re.search(rf'{name}: "((?:[^"\\]|\\.)*)"', text)
assert match is not None, name