fix: settings item ordering, device name width, activity indicator mode, dot position
This commit is contained in:
+16
-21
@@ -146,10 +146,9 @@ const DISPLAY_DEFAULTS = {
|
|||||||
bellSound: false,
|
bellSound: false,
|
||||||
notificationPermission: 'default',
|
notificationPermission: 'default',
|
||||||
viewMode: 'auto',
|
viewMode: 'auto',
|
||||||
showDeviceBadges: true, // show device name labels on tiles/sidebar
|
showDeviceBadges: true, // show device name labels on tiles/sidebar
|
||||||
showActivityGlow: true, // show amber border/glow on tiles/sidebar with bells
|
showHoverPreview: true, // show hover preview popover on tile hover
|
||||||
showHoverPreview: true, // show hover preview popover on tile hover
|
activityIndicator: 'both', // 'none' | 'glow' | 'dot' | 'both'
|
||||||
showActivityDot: true, // show activity dot on tiles/sidebar
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var VIEW_MODES = ['auto', 'fit'];
|
var VIEW_MODES = ['auto', 'fit'];
|
||||||
@@ -460,18 +459,19 @@ function buildTileHTML(session, index, mobile) {
|
|||||||
const unseen = session.bell && session.bell.unseen_count;
|
const unseen = session.bell && session.bell.unseen_count;
|
||||||
|
|
||||||
var ds = loadDisplaySettings();
|
var ds = loadDisplaySettings();
|
||||||
|
var actIndicator = ds.activityIndicator !== undefined ? ds.activityIndicator : 'both';
|
||||||
|
|
||||||
let classes = 'session-tile';
|
let classes = 'session-tile';
|
||||||
if (isBell && ds.showActivityGlow !== false) classes += ' session-tile--bell';
|
if (isBell && (actIndicator === 'glow' || actIndicator === 'both')) classes += ' session-tile--bell';
|
||||||
if (mobile) classes += ` session-tile--tier-${priority}`;
|
if (mobile) classes += ` session-tile--tier-${priority}`;
|
||||||
|
|
||||||
const name = session.name || '';
|
const name = session.name || '';
|
||||||
const escapedName = escapeHtml(name);
|
const escapedName = escapeHtml(name);
|
||||||
const timeStr = formatTimestamp(session.last_activity_at || null);
|
const timeStr = formatTimestamp(session.last_activity_at || null);
|
||||||
|
|
||||||
// Activity dot — absolute positioned in upper-right corner of tile (no count text)
|
// Activity dot — absolute positioned in lower-right corner of tile (no count text)
|
||||||
let bellDotHtml = '';
|
let bellDotHtml = '';
|
||||||
if (unseen && unseen > 0 && ds.showActivityDot !== false) {
|
if (unseen && unseen > 0 && (actIndicator === 'dot' || actIndicator === 'both')) {
|
||||||
bellDotHtml = '<span class="tile-bell-dot"></span>';
|
bellDotHtml = '<span class="tile-bell-dot"></span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,17 +514,18 @@ function buildSidebarHTML(session, currentSession) {
|
|||||||
const isActive = name === currentSession;
|
const isActive = name === currentSession;
|
||||||
|
|
||||||
var ds = loadDisplaySettings();
|
var ds = loadDisplaySettings();
|
||||||
|
var actIndicator = ds.activityIndicator !== undefined ? ds.activityIndicator : 'both';
|
||||||
|
|
||||||
const unseen = session.bell && session.bell.unseen_count;
|
const unseen = session.bell && session.bell.unseen_count;
|
||||||
const isBell = unseen && unseen > 0;
|
const isBell = unseen && unseen > 0;
|
||||||
|
|
||||||
let classes = 'sidebar-item';
|
let classes = 'sidebar-item';
|
||||||
if (isActive) classes += ' sidebar-item--active';
|
if (isActive) classes += ' sidebar-item--active';
|
||||||
if (isBell && ds.showActivityGlow !== false) classes += ' sidebar-item--bell';
|
if (isBell && (actIndicator === 'glow' || actIndicator === 'both')) classes += ' sidebar-item--bell';
|
||||||
|
|
||||||
// Activity dot — inline in sidebar header (not absolute, since items are smaller)
|
// Activity dot — inline in sidebar header (not absolute, since items are smaller)
|
||||||
let bellDotHtml = '';
|
let bellDotHtml = '';
|
||||||
if (isBell && ds.showActivityDot !== false) {
|
if (isBell && (actIndicator === 'dot' || actIndicator === 'both')) {
|
||||||
bellDotHtml = '<span class="tile-bell-dot sidebar-bell-dot"></span>';
|
bellDotHtml = '<span class="tile-bell-dot sidebar-bell-dot"></span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1680,14 +1681,11 @@ function onDisplaySettingChange() {
|
|||||||
var showDeviceBadgesEl = document.getElementById('setting-show-device-badges');
|
var showDeviceBadgesEl = document.getElementById('setting-show-device-badges');
|
||||||
if (showDeviceBadgesEl) ds.showDeviceBadges = showDeviceBadgesEl.checked;
|
if (showDeviceBadgesEl) ds.showDeviceBadges = showDeviceBadgesEl.checked;
|
||||||
|
|
||||||
var showActivityGlowEl = document.getElementById('setting-show-activity-glow');
|
|
||||||
if (showActivityGlowEl) ds.showActivityGlow = showActivityGlowEl.checked;
|
|
||||||
|
|
||||||
var showHoverPreviewEl = document.getElementById('setting-show-hover-preview');
|
var showHoverPreviewEl = document.getElementById('setting-show-hover-preview');
|
||||||
if (showHoverPreviewEl) ds.showHoverPreview = showHoverPreviewEl.checked;
|
if (showHoverPreviewEl) ds.showHoverPreview = showHoverPreviewEl.checked;
|
||||||
|
|
||||||
var showActivityDotEl = document.getElementById('setting-show-activity-dot');
|
var activityIndicatorEl = document.getElementById('setting-activity-indicator');
|
||||||
if (showActivityDotEl) ds.showActivityDot = showActivityDotEl.checked;
|
if (activityIndicatorEl) ds.activityIndicator = activityIndicatorEl.value;
|
||||||
|
|
||||||
saveDisplaySettings(ds);
|
saveDisplaySettings(ds);
|
||||||
applyDisplaySettings(ds);
|
applyDisplaySettings(ds);
|
||||||
@@ -1737,15 +1735,13 @@ function openSettings() {
|
|||||||
const viewModeEl = $('setting-view-mode');
|
const viewModeEl = $('setting-view-mode');
|
||||||
if (viewModeEl) viewModeEl.value = loadGridViewMode();
|
if (viewModeEl) viewModeEl.value = loadGridViewMode();
|
||||||
|
|
||||||
// Populate new display toggle checkboxes
|
// Populate display toggle controls
|
||||||
const showDeviceBadgesEl = $('setting-show-device-badges');
|
const showDeviceBadgesEl = $('setting-show-device-badges');
|
||||||
if (showDeviceBadgesEl) showDeviceBadgesEl.checked = settings.showDeviceBadges !== false;
|
if (showDeviceBadgesEl) showDeviceBadgesEl.checked = settings.showDeviceBadges !== false;
|
||||||
const showActivityGlowEl = $('setting-show-activity-glow');
|
|
||||||
if (showActivityGlowEl) showActivityGlowEl.checked = settings.showActivityGlow !== false;
|
|
||||||
const showHoverPreviewEl = $('setting-show-hover-preview');
|
const showHoverPreviewEl = $('setting-show-hover-preview');
|
||||||
if (showHoverPreviewEl) showHoverPreviewEl.checked = settings.showHoverPreview !== false;
|
if (showHoverPreviewEl) showHoverPreviewEl.checked = settings.showHoverPreview !== false;
|
||||||
const showActivityDotEl = $('setting-show-activity-dot');
|
const activityIndicatorEl = $('setting-activity-indicator');
|
||||||
if (showActivityDotEl) showActivityDotEl.checked = settings.showActivityDot !== false;
|
if (activityIndicatorEl) activityIndicatorEl.value = settings.activityIndicator || 'both';
|
||||||
|
|
||||||
// Populate Sessions tab / bell sound from display settings
|
// Populate Sessions tab / bell sound from display settings
|
||||||
const bellSoundEl = $('setting-bell-sound');
|
const bellSoundEl = $('setting-bell-sound');
|
||||||
@@ -2270,9 +2266,8 @@ function bindStaticEventListeners() {
|
|||||||
on($('setting-hover-delay'), 'change', onDisplaySettingChange);
|
on($('setting-hover-delay'), 'change', onDisplaySettingChange);
|
||||||
on($('setting-grid-columns'), 'change', onDisplaySettingChange);
|
on($('setting-grid-columns'), 'change', onDisplaySettingChange);
|
||||||
on($('setting-show-device-badges'), 'change', onDisplaySettingChange);
|
on($('setting-show-device-badges'), 'change', onDisplaySettingChange);
|
||||||
on($('setting-show-activity-glow'), 'change', onDisplaySettingChange);
|
|
||||||
on($('setting-show-hover-preview'), 'change', onDisplaySettingChange);
|
on($('setting-show-hover-preview'), 'change', onDisplaySettingChange);
|
||||||
on($('setting-show-activity-dot'), 'change', onDisplaySettingChange);
|
on($('setting-activity-indicator'), 'change', onDisplaySettingChange);
|
||||||
on($('setting-view-mode'), 'change', function() {
|
on($('setting-view-mode'), 'change', function() {
|
||||||
var el = $('setting-view-mode');
|
var el = $('setting-view-mode');
|
||||||
if (el) {
|
if (el) {
|
||||||
|
|||||||
+26
-25
@@ -93,6 +93,10 @@
|
|||||||
</nav>
|
</nav>
|
||||||
<div class="settings-content">
|
<div class="settings-content">
|
||||||
<div class="settings-panel" data-tab="display">
|
<div class="settings-panel" data-tab="display">
|
||||||
|
<div class="settings-field">
|
||||||
|
<label class="settings-label" for="setting-device-name">Device Name</label>
|
||||||
|
<input type="text" id="setting-device-name" class="settings-input" placeholder="This device" />
|
||||||
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label class="settings-label" for="setting-font-size">Font Size</label>
|
<label class="settings-label" for="setting-font-size">Font Size</label>
|
||||||
<select id="setting-font-size" class="settings-select">
|
<select id="setting-font-size" class="settings-select">
|
||||||
@@ -104,6 +108,15 @@
|
|||||||
<option value="18">18</option>
|
<option value="18">18</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-field">
|
||||||
|
<label class="settings-label" for="setting-grid-columns">Grid Columns</label>
|
||||||
|
<select id="setting-grid-columns" class="settings-select">
|
||||||
|
<option value="auto" selected>Auto</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="3">3</option>
|
||||||
|
<option value="4">4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label class="settings-label" for="setting-hover-delay">Hover Delay</label>
|
<label class="settings-label" for="setting-hover-delay">Hover Delay</label>
|
||||||
<select id="setting-hover-delay" class="settings-select">
|
<select id="setting-hover-delay" class="settings-select">
|
||||||
@@ -115,33 +128,21 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label class="settings-label" for="setting-grid-columns">Grid Columns</label>
|
<label class="settings-label" for="setting-show-hover-preview">Show hover preview</label>
|
||||||
<select id="setting-grid-columns" class="settings-select">
|
<input type="checkbox" id="setting-show-hover-preview" class="settings-checkbox" checked />
|
||||||
<option value="auto" selected>Auto</option>
|
|
||||||
<option value="2">2</option>
|
|
||||||
<option value="3">3</option>
|
|
||||||
<option value="4">4</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label class="settings-label" for="setting-show-device-badges">Show device badges</label>
|
<label class="settings-label" for="setting-show-device-badges">Show device badges</label>
|
||||||
<input type="checkbox" id="setting-show-device-badges" class="settings-checkbox" checked />
|
<input type="checkbox" id="setting-show-device-badges" class="settings-checkbox" checked />
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label class="settings-label" for="setting-show-activity-glow">Show activity glow</label>
|
<label class="settings-label" for="setting-activity-indicator">Activity indicator</label>
|
||||||
<input type="checkbox" id="setting-show-activity-glow" class="settings-checkbox" checked />
|
<select id="setting-activity-indicator" class="settings-select">
|
||||||
</div>
|
<option value="both">Dot + Glow</option>
|
||||||
<div class="settings-field">
|
<option value="glow">Glow only</option>
|
||||||
<label class="settings-label" for="setting-show-hover-preview">Show hover preview</label>
|
<option value="dot">Dot only</option>
|
||||||
<input type="checkbox" id="setting-show-hover-preview" class="settings-checkbox" checked />
|
<option value="none">None</option>
|
||||||
</div>
|
</select>
|
||||||
<div class="settings-field">
|
|
||||||
<label class="settings-label" for="setting-show-activity-dot">Show activity dot</label>
|
|
||||||
<input type="checkbox" id="setting-show-activity-dot" class="settings-checkbox" checked />
|
|
||||||
</div>
|
|
||||||
<div class="settings-field">
|
|
||||||
<label class="settings-label" for="setting-device-name">Device Name</label>
|
|
||||||
<input type="text" id="setting-device-name" class="settings-input" placeholder="This device" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-panel hidden" data-tab="sessions">
|
<div class="settings-panel hidden" data-tab="sessions">
|
||||||
@@ -159,10 +160,6 @@
|
|||||||
<option value="recent">Recent</option>
|
<option value="recent">Recent</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field settings-field--column">
|
|
||||||
<label class="settings-label">Hidden Sessions</label>
|
|
||||||
<div id="setting-hidden-sessions" class="settings-checkbox-list"></div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label class="settings-label" for="setting-window-size-largest">Auto-set window size (largest)</label>
|
<label class="settings-label" for="setting-window-size-largest">Auto-set window size (largest)</label>
|
||||||
<input type="checkbox" id="setting-window-size-largest" class="settings-checkbox" />
|
<input type="checkbox" id="setting-window-size-largest" class="settings-checkbox" />
|
||||||
@@ -182,6 +179,10 @@
|
|||||||
<button id="notification-request-btn" class="settings-action-btn">Request Permission</button>
|
<button id="notification-request-btn" class="settings-action-btn">Request Permission</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-field settings-field--column">
|
||||||
|
<label class="settings-label">Hidden Sessions</label>
|
||||||
|
<div id="setting-hidden-sessions" class="settings-checkbox-list"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-panel hidden" data-tab="new-session">
|
<div class="settings-panel hidden" data-tab="new-session">
|
||||||
<div class="settings-field settings-field--column">
|
<div class="settings-field settings-field--column">
|
||||||
|
|||||||
@@ -616,7 +616,7 @@ body {
|
|||||||
|
|
||||||
.tile-bell-dot {
|
.tile-bell-dot {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 6px;
|
bottom: 6px;
|
||||||
right: 6px;
|
right: 6px;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
@@ -1684,3 +1684,8 @@ body {
|
|||||||
#multi-device-fields {
|
#multi-device-fields {
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Constrain device name input width so it doesn't stretch the full settings panel */
|
||||||
|
#setting-device-name {
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3870,12 +3870,12 @@ test('DISPLAY_DEFAULTS includes showDeviceBadges key', () => {
|
|||||||
assert.ok(defaultsBody.includes('showDeviceBadges'), 'DISPLAY_DEFAULTS must include showDeviceBadges');
|
assert.ok(defaultsBody.includes('showDeviceBadges'), 'DISPLAY_DEFAULTS must include showDeviceBadges');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DISPLAY_DEFAULTS includes showActivityGlow key', () => {
|
test('DISPLAY_DEFAULTS does not include showActivityGlow (replaced by activityIndicator)', () => {
|
||||||
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||||
const defaultsStart = source.indexOf('DISPLAY_DEFAULTS');
|
const defaultsStart = source.indexOf('DISPLAY_DEFAULTS');
|
||||||
const defaultsEnd = source.indexOf('};', defaultsStart);
|
const defaultsEnd = source.indexOf('};', defaultsStart);
|
||||||
const defaultsBody = source.substring(defaultsStart, defaultsEnd + 2);
|
const defaultsBody = source.substring(defaultsStart, defaultsEnd + 2);
|
||||||
assert.ok(defaultsBody.includes('showActivityGlow'), 'DISPLAY_DEFAULTS must include showActivityGlow');
|
assert.ok(!defaultsBody.includes('showActivityGlow'), 'DISPLAY_DEFAULTS must NOT include showActivityGlow — replaced by activityIndicator');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DISPLAY_DEFAULTS includes showHoverPreview key', () => {
|
test('DISPLAY_DEFAULTS includes showHoverPreview key', () => {
|
||||||
@@ -3886,12 +3886,12 @@ test('DISPLAY_DEFAULTS includes showHoverPreview key', () => {
|
|||||||
assert.ok(defaultsBody.includes('showHoverPreview'), 'DISPLAY_DEFAULTS must include showHoverPreview');
|
assert.ok(defaultsBody.includes('showHoverPreview'), 'DISPLAY_DEFAULTS must include showHoverPreview');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DISPLAY_DEFAULTS includes showActivityDot key', () => {
|
test('DISPLAY_DEFAULTS does not include showActivityDot (replaced by activityIndicator)', () => {
|
||||||
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||||
const defaultsStart = source.indexOf('DISPLAY_DEFAULTS');
|
const defaultsStart = source.indexOf('DISPLAY_DEFAULTS');
|
||||||
const defaultsEnd = source.indexOf('};', defaultsStart);
|
const defaultsEnd = source.indexOf('};', defaultsStart);
|
||||||
const defaultsBody = source.substring(defaultsStart, defaultsEnd + 2);
|
const defaultsBody = source.substring(defaultsStart, defaultsEnd + 2);
|
||||||
assert.ok(defaultsBody.includes('showActivityDot'), 'DISPLAY_DEFAULTS must include showActivityDot');
|
assert.ok(!defaultsBody.includes('showActivityDot'), 'DISPLAY_DEFAULTS must NOT include showActivityDot — replaced by activityIndicator');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('HTML index.html has setting-show-device-badges checkbox', () => {
|
test('HTML index.html has setting-show-device-badges checkbox', () => {
|
||||||
@@ -3899,9 +3899,9 @@ test('HTML index.html has setting-show-device-badges checkbox', () => {
|
|||||||
assert.ok(source.includes('setting-show-device-badges'), 'Display panel must have setting-show-device-badges checkbox');
|
assert.ok(source.includes('setting-show-device-badges'), 'Display panel must have setting-show-device-badges checkbox');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('HTML index.html has setting-show-activity-glow checkbox', () => {
|
test('HTML index.html does not have setting-show-activity-glow checkbox (replaced by activity-indicator)', () => {
|
||||||
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
||||||
assert.ok(source.includes('setting-show-activity-glow'), 'Display panel must have setting-show-activity-glow checkbox');
|
assert.ok(!source.includes('setting-show-activity-glow'), 'Display panel must NOT have setting-show-activity-glow — replaced by setting-activity-indicator');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('HTML index.html has setting-show-hover-preview checkbox', () => {
|
test('HTML index.html has setting-show-hover-preview checkbox', () => {
|
||||||
@@ -3909,9 +3909,9 @@ test('HTML index.html has setting-show-hover-preview checkbox', () => {
|
|||||||
assert.ok(source.includes('setting-show-hover-preview'), 'Display panel must have setting-show-hover-preview checkbox');
|
assert.ok(source.includes('setting-show-hover-preview'), 'Display panel must have setting-show-hover-preview checkbox');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('HTML index.html has setting-show-activity-dot checkbox', () => {
|
test('HTML index.html does not have setting-show-activity-dot checkbox (replaced by activity-indicator)', () => {
|
||||||
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
||||||
assert.ok(source.includes('setting-show-activity-dot'), 'Display panel must have setting-show-activity-dot checkbox');
|
assert.ok(!source.includes('setting-show-activity-dot'), 'Display panel must NOT have setting-show-activity-dot — replaced by setting-activity-indicator');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('CSS style.css has .tile-bell-dot rule', () => {
|
test('CSS style.css has .tile-bell-dot rule', () => {
|
||||||
@@ -3938,15 +3938,133 @@ test('showPreview checks showHoverPreview setting before showing popover', () =>
|
|||||||
assert.ok(fnBody.includes('showHoverPreview'), 'showPreview must check showHoverPreview setting before showing popover');
|
assert.ok(fnBody.includes('showHoverPreview'), 'showPreview must check showHoverPreview setting before showing popover');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('bindStaticEventListeners binds change events for new display toggle checkboxes', () => {
|
test('bindStaticEventListeners binds change events for display toggle controls', () => {
|
||||||
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||||
const fnStart = source.indexOf('function bindStaticEventListeners(');
|
const fnStart = source.indexOf('function bindStaticEventListeners(');
|
||||||
assert.ok(fnStart !== -1, 'bindStaticEventListeners must exist');
|
assert.ok(fnStart !== -1, 'bindStaticEventListeners must exist');
|
||||||
const fnEnd = source.indexOf('\nfunction ', fnStart + 1);
|
const fnEnd = source.indexOf('\nfunction ', fnStart + 1);
|
||||||
const fnBody = source.substring(fnStart, fnEnd > fnStart ? fnEnd : fnStart + 10000);
|
const fnBody = source.substring(fnStart, fnEnd > fnStart ? fnEnd : fnStart + 10000);
|
||||||
assert.ok(fnBody.includes('setting-show-device-badges'), 'must bind setting-show-device-badges');
|
assert.ok(fnBody.includes('setting-show-device-badges'), 'must bind setting-show-device-badges');
|
||||||
assert.ok(fnBody.includes('setting-show-activity-glow'), 'must bind setting-show-activity-glow');
|
|
||||||
assert.ok(fnBody.includes('setting-show-hover-preview'), 'must bind setting-show-hover-preview');
|
assert.ok(fnBody.includes('setting-show-hover-preview'), 'must bind setting-show-hover-preview');
|
||||||
assert.ok(fnBody.includes('setting-show-activity-dot'), 'must bind setting-show-activity-dot');
|
assert.ok(fnBody.includes('setting-activity-indicator'), 'must bind setting-activity-indicator');
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Activity indicator dropdown (replaces showActivityGlow + showActivityDot toggles) ---
|
||||||
|
|
||||||
|
test('DISPLAY_DEFAULTS includes activityIndicator key with value both', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||||
|
const defaultsStart = source.indexOf('DISPLAY_DEFAULTS');
|
||||||
|
const defaultsEnd = source.indexOf('};', defaultsStart);
|
||||||
|
const defaultsBody = source.substring(defaultsStart, defaultsEnd + 2);
|
||||||
|
assert.ok(defaultsBody.includes('activityIndicator'), 'DISPLAY_DEFAULTS must include activityIndicator');
|
||||||
|
assert.ok(defaultsBody.includes("'both'") || defaultsBody.includes('"both"'), "DISPLAY_DEFAULTS activityIndicator default must be 'both'");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('HTML index.html has setting-activity-indicator select element', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
||||||
|
assert.ok(source.includes('setting-activity-indicator'), 'Display panel must have setting-activity-indicator select');
|
||||||
|
assert.ok(source.includes('value="both"'), 'must have Dot + Glow option with value both');
|
||||||
|
assert.ok(source.includes('value="glow"'), 'must have Glow only option');
|
||||||
|
assert.ok(source.includes('value="dot"'), 'must have Dot only option');
|
||||||
|
assert.ok(source.includes('value="none"'), 'must have None option');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML shows tile-bell-dot when activityIndicator is dot', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'dot' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(html.includes('tile-bell-dot'), 'tile-bell-dot must appear when activityIndicator is dot');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML shows tile-bell-dot when activityIndicator is both', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'both' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(html.includes('tile-bell-dot'), 'tile-bell-dot must appear when activityIndicator is both');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML omits tile-bell-dot when activityIndicator is none', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'none' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(!html.includes('tile-bell-dot'), 'tile-bell-dot must NOT appear when activityIndicator is none');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML omits tile-bell-dot when activityIndicator is glow', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'glow' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(!html.includes('tile-bell-dot'), 'tile-bell-dot must NOT appear when activityIndicator is glow');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML adds session-tile--bell when activityIndicator is glow', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'glow' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(html.includes('session-tile--bell'), 'session-tile--bell must appear when activityIndicator is glow');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML adds session-tile--bell when activityIndicator is both', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'both' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(html.includes('session-tile--bell'), 'session-tile--bell must appear when activityIndicator is both');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML omits session-tile--bell when activityIndicator is none', () => {
|
||||||
|
_localStorageStore['muxplex.display'] = JSON.stringify({ activityIndicator: 'none' });
|
||||||
|
const session = { name: 's', bell: { unseen_count: 1, seen_at: null, last_fired_at: 100 }, snapshot: '' };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
assert.ok(!html.includes('session-tile--bell'), 'session-tile--bell must NOT appear when activityIndicator is none');
|
||||||
|
_localStorageStore = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CSS style.css has #setting-device-name max-width rule', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../style.css', import.meta.url), 'utf8');
|
||||||
|
assert.ok(source.includes('#setting-device-name'), 'style.css must have #setting-device-name rule');
|
||||||
|
assert.ok(source.includes('max-width'), 'style.css #setting-device-name rule must include max-width');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CSS style.css .tile-bell-dot uses bottom positioning not top', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../style.css', import.meta.url), 'utf8');
|
||||||
|
// Find the .tile-bell-dot rule block
|
||||||
|
const ruleStart = source.indexOf('.tile-bell-dot {');
|
||||||
|
assert.ok(ruleStart !== -1, '.tile-bell-dot rule must exist');
|
||||||
|
const ruleEnd = source.indexOf('}', ruleStart);
|
||||||
|
const ruleBody = source.substring(ruleStart, ruleEnd + 1);
|
||||||
|
assert.ok(ruleBody.includes('bottom:') || ruleBody.includes('bottom :'), '.tile-bell-dot must use bottom positioning');
|
||||||
|
assert.ok(!ruleBody.includes('top: 6px'), '.tile-bell-dot must NOT use top: 6px');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('HTML Display panel device name field appears before font size field', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
||||||
|
const displayPanelStart = source.indexOf('<div class="settings-panel" data-tab="display"');
|
||||||
|
assert.ok(displayPanelStart !== -1, 'display panel must exist');
|
||||||
|
const nextPanel = source.indexOf('<div class="settings-panel', displayPanelStart + 1);
|
||||||
|
const displayPanelContent = source.substring(displayPanelStart, nextPanel !== -1 ? nextPanel : displayPanelStart + 3000);
|
||||||
|
const deviceNameIdx = displayPanelContent.indexOf('setting-device-name');
|
||||||
|
const fontSizeIdx = displayPanelContent.indexOf('setting-font-size');
|
||||||
|
assert.ok(deviceNameIdx !== -1, 'device name field must be in display panel');
|
||||||
|
assert.ok(fontSizeIdx !== -1, 'font size field must be in display panel');
|
||||||
|
assert.ok(deviceNameIdx < fontSizeIdx, 'device name must appear before font size in display panel');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('HTML Sessions panel hidden sessions field appears after bell sound', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
||||||
|
const sessionsPanelStart = source.indexOf('<div class="settings-panel hidden" data-tab="sessions"');
|
||||||
|
assert.ok(sessionsPanelStart !== -1, 'sessions panel must exist');
|
||||||
|
const nextPanel = source.indexOf('<div class="settings-panel', sessionsPanelStart + 1);
|
||||||
|
const sessionsPanelContent = source.substring(sessionsPanelStart, nextPanel !== -1 ? nextPanel : sessionsPanelStart + 4000);
|
||||||
|
const hiddenIdx = sessionsPanelContent.indexOf('setting-hidden-sessions');
|
||||||
|
const bellSoundIdx = sessionsPanelContent.indexOf('setting-bell-sound');
|
||||||
|
assert.ok(hiddenIdx !== -1, 'hidden sessions must be in sessions panel');
|
||||||
|
assert.ok(bellSoundIdx !== -1, 'bell sound must be in sessions panel');
|
||||||
|
assert.ok(hiddenIdx > bellSoundIdx, 'hidden sessions must appear after bell sound (i.e., near the end)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user