refactor(web): remove deprecated class names and hoist map marker colors

Zero-visual-delta cleanups:

- Drop input-bordered/select-bordered (removed in daisyUI v5; emitted
  no CSS) from all inputs and selects.
- Rename bare `shadow` to `shadow-sm` — in Tailwind v4 bare `shadow` is
  a deprecated alias with the identical value.
- Hoist the hardcoded map marker hex colors into CSS variables in the
  app.css palette block (same values; markers sit on map tiles and stay
  theme-independent by design).
- Convert the five text-base-content/* outliers to the repo's dominant
  opacity-* muted-text idiom (identical rendering on plain text).
- Remove a dead ternary in renderNodeDisplay and an unused iconLock
  import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Louis King
2026-07-02 21:30:07 +01:00
parent e2c7a0f987
commit bb929dfe5f
14 changed files with 55 additions and 49 deletions
+6
View File
@@ -29,6 +29,12 @@
--color-members: oklch(0.72 0.17 50); /* orange */
--color-neutral: oklch(0.3 0.01 250); /* subtle dark grey */
--color-radio: oklch(0.75 0.15 210); /* cyan — radio tile icons */
/* Map marker colors — theme-independent, chosen for contrast on map tiles */
--color-marker-infra: #3b82f6;
--color-marker-infra-border: #1e40af;
--color-marker-public: #22c55e;
--color-marker-public-border: #15803d;
}
/* Light mode: darker section colors for contrast on light backgrounds */
@@ -74,7 +74,7 @@ export function mobileSortSelect({ currentSort, currentOrder, navigate, basePath
return html`<div class="lg:hidden mb-5">
<div class="flex items-center gap-2">
<span class="text-xs opacity-60">${t('common.sort_by')}</span>
<select class="select select-sm select-bordered flex-1"
<select class="select select-sm flex-1"
@change=${onChange}>
${sortOptions}
</select>
@@ -402,7 +402,7 @@ export function copyToClipboard(e, text) {
export function renderNodeDisplay({ name, description, publicKey, advType, size = 'base' }) {
const displayName = name || null;
const emoji = getNodeEmoji(name, advType);
const emojiSize = size === 'sm' ? 'text-lg' : 'text-lg';
const emojiSize = 'text-lg';
const nameSize = size === 'sm' ? 'text-sm' : 'text-base';
const descSize = size === 'sm' ? 'text-xs' : 'text-xs';
@@ -718,7 +718,7 @@ export function renderAuthSection(container, config) {
<div tabindex="0" role="button" class="btn btn-ghost btn-circle btn-sm avatar">
${pictureHtml}
</div>
<ul tabindex="0" class="dropdown-content menu z-50 p-2 shadow bg-base-100 rounded-box w-56 mt-3">
<ul tabindex="0" class="dropdown-content menu z-50 p-2 shadow-sm bg-base-100 rounded-box w-56 mt-3">
<li class="menu-title">
<div class="flex flex-col gap-1">
<span class="font-medium">${displayName}</span>
@@ -760,7 +760,7 @@ export function renderFilterCard({ fields, basePath, navigate, submitLabel, clea
if (!collapsible) {
return html`
<div class="card shadow mb-6 panel-solid" style="--panel-color: var(--color-neutral)">
<div class="card shadow-sm mb-6 panel-solid" style="--panel-color: var(--color-neutral)">
<div class="card-body py-4">${formBody}</div>
</div>
`;
@@ -790,7 +790,7 @@ export function renderFilterCard({ fields, basePath, navigate, submitLabel, clea
*/
export function renderStatCard({ icon, color, title, value, description }) {
return html`
<div class="stat bg-base-200 rounded-box shadow panel-glow" style="--panel-color: ${color}">
<div class="stat bg-base-200 rounded-box shadow-sm panel-glow" style="--panel-color: ${color}">
<div class="stat-figure" style="color: ${color}">${icon}</div>
<div class="stat-title">${title}</div>
<div class="stat-value" style="color: ${color}">${value}</div>
@@ -226,14 +226,14 @@ ${displayContent}`, container);
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('common.search')}</span>
</label>
<input type="text" name="search" .value=${search} placeholder="${t('common.search_placeholder')}" class="input input-bordered input-sm w-80" @keydown=${submitOnEnter} />
<input type="text" name="search" .value=${search} placeholder="${t('common.search_placeholder')}" class="input input-sm w-80" @keydown=${submitOnEnter} />
</div>`,
() => html`
<div class="flex flex-col gap-1 max-w-48">
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('advertisements.filter_route_type_label')}</span>
</label>
<select name="route_type" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="route_type" class="select select-sm" @change=${autoSubmit}>
<option value="flood,transport_flood" ?selected=${route_type === 'flood,transport_flood'}>${t('advertisements.route_type_flood')}</option>
<option value="all" ?selected=${route_type === 'all'}>${t('advertisements.route_type_all')}</option>
<option value="direct" ?selected=${route_type === 'direct'}>${t('advertisements.route_type_direct')}</option>
@@ -246,7 +246,7 @@ ${displayContent}`, container);
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('common.filter_operator_label')}</span>
</label>
<select name="adopted_by" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="adopted_by" class="select select-sm" @change=${autoSubmit}>
<option value="" ?selected=${!adopted_by}>${t('common.all_operators')}</option>
${profiles.sort((a, b) => {
const na = a.name || a.callsign || '';
@@ -301,7 +301,7 @@ ${observerBadges('flex lg:hidden mb-4')}
${mobileCards}
</div>
<div class="hidden lg:block overflow-x-auto overflow-y-visible bg-base-100 rounded-box shadow">
<div class="hidden lg:block overflow-x-auto overflow-y-visible bg-base-100 rounded-box shadow-sm">
<table class="table table-zebra">
<thead>
<tr>
@@ -1,6 +1,6 @@
import { apiGet, apiPost, apiPut, apiDelete, isAbortError } from '../api.js';
import { html, litRender, nothing, t, errorAlert, getConfig, hasRole } from '../components.js';
import { iconChannel, iconPlus, iconEdit, iconTrash, iconLock } from '../icons.js';
import { iconChannel, iconPlus, iconEdit, iconTrash } from '../icons.js';
const VISIBILITY_ORDER = ['community', 'member', 'operator', 'admin'];
@@ -70,19 +70,19 @@ function renderChannelModal({ channel, isEdit, onSave, onCancel }) {
<form @submit=${(e) => { e.preventDefault(); onSave(); }}>
<div class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-3 items-center mb-4">
<label class="text-sm opacity-70 text-right">${t('channels.name_label')}</label>
<input type="text" id="channel-modal-name" class="input input-bordered input-sm"
<input type="text" id="channel-modal-name" class="input input-sm"
.value=${isEdit ? channel.name : ''}
?disabled=${isEdit}
placeholder="${t('channels.name_label')}"
required maxlength="100" />
${!isEdit ? html`
<label class="text-sm opacity-70 text-right">${t('channels.key_label')}</label>
<input type="text" id="channel-modal-key" class="input input-bordered input-sm font-mono"
<input type="text" id="channel-modal-key" class="input input-sm font-mono"
placeholder="e.g. ABCDEF0123456789..."
required minlength="32" maxlength="64"
pattern="[0-9A-Fa-f]{32,64}" />` : nothing}
<label class="text-sm opacity-70 text-right">${t('channels.visibility_label')}</label>
<select id="channel-modal-visibility" class="select select-bordered select-sm">
<select id="channel-modal-visibility" class="select select-sm">
<option value="community" .selected=${channel?.visibility === 'community' || !channel}>community</option>
<option value="member" .selected=${channel?.visibility === 'member'}>member</option>
<option value="operator" .selected=${channel?.visibility === 'operator'}>operator</option>
@@ -20,7 +20,7 @@ export async function render(container, params, router) {
<img src=${config.logo_url} alt=${config.network_name} class="${logoClass} h-16 w-16" />
<h1 class="text-3xl font-bold">${config.network_name}</h1>
<h2 class="text-xl font-semibold text-warning">${t('maintenance.title')}</h2>
<p class="text-base-content/70">${t('maintenance.message')}</p>
<p class="opacity-70">${t('maintenance.message')}</p>
</div>
</div>
</div>`, container);
@@ -50,8 +50,8 @@ function createNodeIcon(node, oidcEnabled) {
const timeDisplay = relativeTime ? ' (' + relativeTime + ')' : '';
const iconHtml = (oidcEnabled && node.is_adopted)
? '<div style="width: 12px; height: 12px; background: #3b82f6; border: 2px solid #1e40af; border-radius: 50%; box-shadow: 0 0 4px rgba(59,130,246,0.6), 0 1px 2px rgba(0,0,0,0.5);"></div>'
: '<div style="width: 12px; height: 12px; background: #22c55e; border: 2px solid #15803d; border-radius: 50%; box-shadow: 0 0 4px rgba(34,197,94,0.6), 0 1px 2px rgba(0,0,0,0.5);"></div>';
? '<div style="width: 12px; height: 12px; background: var(--color-marker-infra); border: 2px solid var(--color-marker-infra-border); border-radius: 50%; box-shadow: 0 0 4px rgba(59,130,246,0.6), 0 1px 2px rgba(0,0,0,0.5);"></div>'
: '<div style="width: 12px; height: 12px; background: var(--color-marker-public); border: 2px solid var(--color-marker-public-border); border-radius: 50%; box-shadow: 0 0 4px rgba(34,197,94,0.6), 0 1px 2px rgba(0,0,0,0.5);"></div>';
return L.divIcon({
className: 'custom-div-icon',
@@ -72,8 +72,8 @@ function createPopupContent(node, oidcEnabled) {
let infraIndicatorHtml = '';
if (oidcEnabled && typeof node.is_adopted !== 'undefined') {
const dotColor = node.is_adopted ? '#3b82f6' : '#22c55e';
const borderColor = node.is_adopted ? '#1e40af' : '#15803d';
const dotColor = node.is_adopted ? 'var(--color-marker-infra)' : 'var(--color-marker-public)';
const borderColor = node.is_adopted ? 'var(--color-marker-infra-border)' : 'var(--color-marker-public-border)';
const title = node.is_adopted ? ((window.t && window.t('map.infrastructure')) || 'Infrastructure') : ((window.t && window.t('map.public')) || 'Public');
infraIndicatorHtml = ' <span style="display: inline-block; width: 10px; height: 10px; background: ' + dotColor + '; border: 2px solid ' + borderColor + '; border-radius: 50%; vertical-align: middle;" title="' + title + '"></span>';
}
@@ -206,14 +206,14 @@ export async function render(container, params, router) {
<div class="flex gap-4 flex-wrap items-end">
<div class="fieldset">
<label class="fieldset-label">${t('common.show')}</label>
<select id="filter-category" class="select select-bordered select-sm" @change=${applyFilters}>
<select id="filter-category" class="select select-sm" @change=${applyFilters}>
<option value="">${t('common.all_entity', { entity: t('entities.nodes') })}</option>
${config.oidc_enabled ? html`<option value="infra">${t('map.infrastructure_only')}</option>` : nothing}
</select>
</div>
<div class="fieldset">
<label class="fieldset-label">${t('common.node_type')}</label>
<select id="filter-type" class="select select-bordered select-sm" @change=${applyFilters}>
<select id="filter-type" class="select select-sm" @change=${applyFilters}>
<option value="">${t('common.all_types')}</option>
<option value="chat">${t('node_types.chat')}</option>
<option value="repeater">${t('node_types.repeater')}</option>
@@ -223,7 +223,7 @@ export async function render(container, params, router) {
${config.oidc_enabled && operatorProfiles.length > 0 ? html`
<div class="fieldset">
<label class="fieldset-label">${t('common.filter_operator_label')}</label>
<select id="member-filter" class="select select-bordered select-sm" @change=${applyFilters}>
<select id="member-filter" class="select select-sm" @change=${applyFilters}>
<option value="">${t('common.all_operators')}</option>
${operatorProfiles.sort((a, b) => {
const na = a.name || a.callsign || '';
@@ -255,11 +255,11 @@ ${config.oidc_enabled ? html`
<div class="mt-4 flex flex-wrap gap-4 items-center text-sm">
<span class="opacity-70">${t('map.legend')}</span>
<div class="flex items-center gap-1">
<div style="width: 10px; height: 10px; background: #3b82f6; border: 2px solid #1e40af; border-radius: 50%;"></div>
<div style="width: 10px; height: 10px; background: var(--color-marker-infra); border: 2px solid var(--color-marker-infra-border); border-radius: 50%;"></div>
<span>${t('map.infrastructure')}</span>
</div>
<div class="flex items-center gap-1">
<div style="width: 10px; height: 10px; background: #22c55e; border: 2px solid #15803d; border-radius: 50%;"></div>
<div style="width: 10px; height: 10px; background: var(--color-marker-public); border: 2px solid var(--color-marker-public-border); border-radius: 50%;"></div>
<span>${t('map.public')}</span>
</div>
</div>
@@ -386,7 +386,7 @@ ${displayContent}`, container);
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('common.type')}</span>
</label>
<select name="message_type" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="message_type" class="select select-sm" @change=${autoSubmit}>
<option value="">${t('common.all_types')}</option>
<option value="contact" ?selected=${message_type === 'contact'}>${t('messages.type_direct')}</option>
<option value="channel" ?selected=${message_type === 'channel'}>${t('messages.type_channel')}</option>
@@ -397,7 +397,7 @@ ${displayContent}`, container);
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('entities.channel')}</span>
</label>
<select name="channel_idx" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="channel_idx" class="select select-sm" @change=${autoSubmit}>
<option value="">${t('common.all_channels')}</option>
${builtinLabels.size > 0 ? html`<optgroup label=${t('channels.optgroup_standard')}>${[...builtinLabels.entries()].map(([idx, label]) =>
html`<option value=${idx} ?selected=${channel_idx === String(idx)}>${label}</option>`
@@ -466,7 +466,7 @@ ${observerBadges('flex lg:hidden mb-4')}
${mobileCards}
</div>
<div class="hidden lg:block overflow-x-auto overflow-y-visible bg-base-100 rounded-box shadow">
<div class="hidden lg:block overflow-x-auto overflow-y-visible bg-base-100 rounded-box shadow-sm">
<table class="table table-zebra">
<thead>
<tr>
@@ -49,12 +49,12 @@ function renderEditTagModal() {
<input type="hidden" id="tagEditKey">
<div class="fieldset mb-4">
<label class="fieldset-label">${t('common.value')}</label>
<input type="text" id="tagEditValue" class="input input-bordered w-full">
<input type="text" id="tagEditValue" class="input w-full">
<div class="hidden text-xs text-error" id="tagEditError"></div>
</div>
<div class="fieldset mb-4">
<label class="fieldset-label">${t('common.type')}</label>
<select id="tagEditType" class="select select-bordered w-full">
<select id="tagEditType" class="select w-full">
<option value="string">string</option>
<option value="number">number</option>
<option value="boolean">boolean</option>
@@ -227,13 +227,13 @@ export async function render(container, params, router) {
? html`<form id="tag-add-form" class="mt-4">
<div class="grid grid-cols-1 sm:grid-cols-[1fr_1fr_auto_auto] gap-2 items-end">
<div class="fieldset">
<input type="text" name="key" class="input input-bordered input-sm w-full" placeholder=${t('common.key')} required>
<input type="text" name="key" class="input input-sm w-full" placeholder=${t('common.key')} required>
</div>
<div class="fieldset">
<input type="text" name="value" class="input input-bordered input-sm w-full" placeholder=${t('common.value')}>
<input type="text" name="value" class="input input-sm w-full" placeholder=${t('common.value')}>
<div class="hidden text-xs text-error" id="tagAddError"></div>
</div>
<select name="value_type" class="select select-bordered select-sm w-28">
<select name="value_type" class="select select-sm w-28">
<option value="string">string</option>
<option value="number">number</option>
<option value="boolean">boolean</option>
@@ -297,7 +297,7 @@ export async function render(container, params, router) {
<span class="text-6xl flex-shrink-0" title=${node.adv_type || t('node_types.unknown')}>${emoji}</span>
<div class="flex-1 min-w-0">
<h1 class="text-3xl font-bold">${displayName}</h1>
${tagDescription ? html`<p class="text-base-content/70 mt-2">${tagDescription}</p>` : nothing}
${tagDescription ? html`<p class="opacity-70 mt-2">${tagDescription}</p>` : nothing}
</div>
</div>
@@ -136,14 +136,14 @@ ${displayContent}`, container);
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('common.search')}</span>
</label>
<input type="text" name="search" .value=${search} placeholder="${t('common.search_placeholder')}" class="input input-bordered input-sm w-80" @keydown=${submitOnEnter} />
<input type="text" name="search" .value=${search} placeholder="${t('common.search_placeholder')}" class="input input-sm w-80" @keydown=${submitOnEnter} />
</div>`,
() => html`
<div class="flex flex-col gap-1">
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('common.type')}</span>
</label>
<select name="adv_type" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="adv_type" class="select select-sm" @change=${autoSubmit}>
<option value="">${t('common.all_types')}</option>
<option value="chat" ?selected=${adv_type === 'chat'}>${t('node_types.chat')}</option>
<option value="repeater" ?selected=${adv_type === 'repeater'}>${t('node_types.repeater')}</option>
@@ -158,7 +158,7 @@ ${displayContent}`, container);
<label class="flex items-center py-1">
<span class="opacity-80 text-sm">${t('common.filter_operator_label')}</span>
</label>
<select name="adopted_by" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="adopted_by" class="select select-sm" @change=${autoSubmit}>
<option value="" ?selected=${!adopted_by}>${t('common.all_operators')}</option>
${profiles.sort((a, b) => {
const na = a.name || a.callsign || '';
@@ -210,7 +210,7 @@ ${mobileSortSelect({
${mobileCards}
</div>
<div class="hidden lg:block overflow-x-auto bg-base-100 rounded-box shadow">
<div class="hidden lg:block overflow-x-auto bg-base-100 rounded-box shadow-sm">
<table class="table table-zebra">
<thead>
<tr>
@@ -8,7 +8,7 @@ export async function render(container, params, router) {
<div class="max-w-md">
<div class="text-9xl font-bold text-primary opacity-20">404</div>
<h1 class="text-4xl font-bold -mt-8">${t('common.page_not_found')}</h1>
<p class="py-6 text-base-content/70">
<p class="py-6 opacity-70">
${t('not_found.description')}
</p>
<div class="flex gap-4 justify-center">
@@ -85,7 +85,7 @@ ${content}`, container);
shell(html`
${redactedNotice}
<div class="card bg-base-100 shadow">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8">
${field(t('common.time'), formatDateTime(p.received_at))}
@@ -327,7 +327,7 @@ ${content}`, container);
shell(html`
${redactedNotice}
<div class="card bg-base-100 shadow">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8">
${field(t('common.time'), formatDateTime(g.first_seen))}
@@ -152,12 +152,12 @@ ${displayContent}`, container);
() => html`
<div class="flex flex-col gap-1">
<label class="flex items-center py-1"><span class="opacity-80 text-sm">${t('common.search')}</span></label>
<input type="text" name="search" .value=${search} placeholder="${t('common.search_placeholder')}" class="input input-bordered input-sm w-80" @keydown=${submitOnEnter} />
<input type="text" name="search" .value=${search} placeholder="${t('common.search_placeholder')}" class="input input-sm w-80" @keydown=${submitOnEnter} />
</div>`,
() => html`
<div class="flex flex-col gap-1 max-w-48">
<label class="flex items-center py-1"><span class="opacity-80 text-sm">${t('packets.filter_event_type')}</span></label>
<select name="event_type" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="event_type" class="select select-sm" @change=${autoSubmit}>
<option value="" ?selected=${!event_type}>${t('common.all_types')}</option>
${EVENT_TYPES.map(et => html`<option value=${et} ?selected=${event_type === et}>${et}</option>`)}
</select>
@@ -165,7 +165,7 @@ ${displayContent}`, container);
() => html`
<div class="flex flex-col gap-1 max-w-48">
<label class="flex items-center py-1"><span class="opacity-80 text-sm">${t('entities.channel')}</span></label>
<select name="channel_idx" class="select select-bordered select-sm" @change=${autoSubmit}>
<select name="channel_idx" class="select select-sm" @change=${autoSubmit}>
<option value="" ?selected=${channel_idx === ''}>${t('common.all_channels')}</option>
${channelList.map(c => html`<option value=${c.idx} ?selected=${String(channel_idx) === String(c.idx)}>${c.name} (${c.idx})</option>`)}
</select>
@@ -208,7 +208,7 @@ ${mobileSortSelect({
${mobileCards}
</div>
<div class="hidden lg:block overflow-x-auto overflow-y-visible bg-base-100 rounded-box shadow">
<div class="hidden lg:block overflow-x-auto overflow-y-visible bg-base-100 rounded-box shadow-sm">
<table class="table table-zebra">
<thead>
<tr>
@@ -44,7 +44,7 @@ function renderProfileDetails(profile, config) {
<h2 class="card-title">${t('user_profile.adopted_nodes')}</h2>
${profile.nodes && profile.nodes.length > 0
? html`<div class="space-y-2">${profile.nodes.map(n => renderAdoptedNode(n))}</div>`
: html`<p class="text-base-content/60 text-sm py-4">${t('user_profile.no_adopted_nodes')}</p>`}
: html`<p class="opacity-60 text-sm py-4">${t('user_profile.no_adopted_nodes')}</p>`}
</div>
</div>`
: nothing;
@@ -125,25 +125,25 @@ ${flashHtml}
<form id="profile-form" class="py-4 space-y-4">
<label class="flex items-center gap-3 py-1">
<span class="text-sm font-medium shrink-0 w-24">${t('user_profile.name_label')}</span>
<input type="text" name="name" class="input input-bordered flex-1"
<input type="text" name="name" class="input flex-1"
value=${profile.name || ''}
placeholder=${t('user_profile.name_placeholder')} maxlength="255" />
</label>
<label class="flex items-center gap-3 py-1">
<span class="text-sm font-medium shrink-0 w-24">${t('user_profile.callsign_label')}</span>
<input type="text" name="callsign" class="input input-bordered flex-1"
<input type="text" name="callsign" class="input flex-1"
value=${profile.callsign || ''}
placeholder=${t('user_profile.callsign_placeholder')} maxlength="20" />
</label>
<label class="flex items-center gap-3 py-1">
<span class="text-sm font-medium shrink-0 w-24">${t('user_profile.description_label')}</span>
<input type="text" name="description" class="input input-bordered flex-1"
<input type="text" name="description" class="input flex-1"
value=${profile.description || ''}
placeholder=${t('user_profile.description_placeholder')} maxlength="500" />
</label>
<label class="flex items-center gap-3 py-1">
<span class="text-sm font-medium shrink-0 w-24">${t('user_profile.url_label')}</span>
<input type="url" name="url" class="input input-bordered flex-1"
<input type="url" name="url" class="input flex-1"
value=${profile.url || ''}
placeholder=${t('user_profile.url_placeholder')} maxlength="2048" />
</label>
@@ -160,7 +160,7 @@ ${flashHtml}
<h2 class="card-title">${t('user_profile.adopted_nodes')}</h2>
${profile.nodes && profile.nodes.length > 0
? html`<div class="space-y-2">${profile.nodes.map(n => renderAdoptedNode(n))}</div>`
: html`<p class="text-base-content/60 text-sm py-4">${t('user_profile.no_adopted_nodes')}</p>`}
: html`<p class="opacity-60 text-sm py-4">${t('user_profile.no_adopted_nodes')}</p>`}
</div>
</div>` : nothing}