feat: improve SettingsPage styles with deep-scoping and add libseccomp to Docker setup scripts

This commit is contained in:
Ivan 2026-07-24 11:39:59 -05:00
parent f89f91e267
commit 835667dba3
No known key found for this signature in database
6 changed files with 80 additions and 27 deletions

Binary file not shown.

View file

@ -5265,70 +5265,74 @@ export default {
<style scoped>
@reference "../../style.css";
.settings-section {
/*
* Shared settings chrome is used by extracted section components.
* :deep is required so scoped styles still reach their markup.
*/
:deep(.settings-section) {
@apply w-full border-b border-gray-200/60 dark:border-zinc-800/60 py-6 sm:py-8 flex flex-col break-inside-avoid;
}
.settings-section--hero {
:deep(.settings-section--hero) {
@apply border-b border-gray-200/60 dark:border-zinc-800/60 py-6 sm:py-8;
}
.settings-section__header {
:deep(.settings-section__header) {
@apply flex items-center justify-between gap-3 pb-4 border-b border-gray-100/60 dark:border-zinc-800/60;
}
.settings-section__header h2 {
:deep(.settings-section__header h2) {
@apply text-lg font-semibold text-gray-900 dark:text-white;
}
.settings-section__header p {
:deep(.settings-section__header p) {
@apply text-sm text-gray-600 dark:text-gray-400;
}
.settings-section__eyebrow {
:deep(.settings-section__eyebrow) {
@apply text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400;
}
.settings-section__body {
:deep(.settings-section__body) {
@apply pt-4 text-gray-900 dark:text-gray-100;
}
.input-field {
:deep(.input-field) {
@apply bg-gray-50/90 dark:bg-zinc-800/80 border border-gray-200 dark:border-zinc-700 text-sm rounded-2xl focus:ring-2 focus:ring-blue-400 focus:border-blue-400 dark:focus:ring-blue-500 dark:focus:border-blue-500 block w-full p-2.5 text-gray-900 dark:text-gray-100 transition;
}
.btn-maintenance {
:deep(.btn-maintenance) {
@apply w-full px-4 py-3 rounded-2xl border transition flex items-center justify-between;
}
.setting-toggle {
@apply relative flex flex-row-reverse items-center gap-3 rounded-2xl border border-gray-200 dark:border-zinc-800 bg-white/70 dark:bg-zinc-900/70 px-3 py-3;
:deep(.setting-toggle) {
@apply relative flex flex-row-reverse items-start gap-3 rounded-2xl border border-gray-200 dark:border-zinc-800 bg-white/70 dark:bg-zinc-900/70 px-3 py-3;
}
.setting-toggle > :deep(label) {
:deep(.setting-toggle > label) {
@apply shrink-0 self-center;
}
.setting-toggle :deep(.sr-only) {
:deep(.setting-toggle .sr-only) {
@apply absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap border-0;
}
.setting-toggle__label {
:deep(.setting-toggle__label) {
@apply flex-1 min-w-0 flex flex-col gap-0.5;
}
.setting-toggle__title {
@apply text-sm font-semibold text-gray-900 dark:text-white break-words;
:deep(.setting-toggle__title) {
@apply text-sm font-semibold text-gray-900 dark:text-white break-words leading-snug;
}
.setting-toggle__description {
@apply text-sm text-gray-600 dark:text-gray-300 break-words;
:deep(.setting-toggle__description) {
@apply text-xs sm:text-sm text-gray-600 dark:text-gray-300 break-words leading-snug;
}
.setting-toggle__hint {
:deep(.setting-toggle__hint) {
@apply text-xs text-gray-500 dark:text-gray-400 break-words;
}
.info-callout {
:deep(.info-callout) {
@apply rounded-2xl border border-blue-100 dark:border-blue-900/40 bg-blue-50/60 dark:bg-blue-900/20 px-3 py-3 text-blue-900 dark:text-blue-100;
}
.monospace-field {
:deep(.monospace-field) {
font-family: "Roboto Mono", monospace;
}
.address-card {
:deep(.address-card) {
@apply relative border border-gray-200/70 dark:border-zinc-800/80 py-3 px-3 sm:rounded-xl sm:bg-black/2 dark:sm:bg-white/2 space-y-2;
}
.address-card__label {
:deep(.address-card__label) {
@apply text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400;
}
.address-card__value {
:deep(.address-card__value) {
@apply text-sm text-gray-900 dark:text-white wrap-break-word pr-16;
}
.address-card__action {
:deep(.address-card__action) {
@apply absolute top-3 right-3 inline-flex items-center gap-1 rounded-full border border-gray-200 dark:border-zinc-700 px-3 py-1 text-xs font-semibold text-gray-700 dark:text-gray-100 bg-white/70 dark:bg-zinc-900/60 hover:border-blue-400 dark:hover:border-blue-500 transition;
}
.fade-enter-active,

View file

@ -3,7 +3,7 @@
# Chainguard/Wolfi final-stage packages, pip bootstrap, and meshchat user.
set -eu
apk add --no-cache opus libffi shadow
apk add --no-cache opus libffi shadow libseccomp
pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.context>=6.1.0"
rm -rf /root/.cache/pip

View file

@ -7,7 +7,7 @@ set -eu
VARIANT="${1:-${VARIANT:-standard}}"
apk upgrade --no-cache
apk add --no-cache opusfile libffi espeak-ng su-exec
apk add --no-cache opusfile libffi espeak-ng su-exec libseccomp
case "${VARIANT}" in
standard) ;;

View file

@ -105,6 +105,20 @@ describe("behavior contracts: user-visible wiring must stay connected", () => {
expect(util).toContain("LOCAL_BACKEND_URL");
});
it("SettingsPage deep-scopes shared toggle styles for extracted sections", () => {
const src = readSource("meshchatx/src/frontend/components/settings/SettingsPage.vue");
expect(src).toContain(":deep(.setting-toggle)");
expect(src).toContain(":deep(.setting-toggle__label)");
expect(src).toContain(":deep(.setting-toggle__title)");
});
it("Docker runtime images install libseccomp for Seccomp-BPF", () => {
const alpine = readSource("scripts/docker/runtime-setup.sh");
const chainguard = readSource("scripts/docker/runtime-setup-chainguard.sh");
expect(alpine).toMatch(/apk add[^\n]*libseccomp/);
expect(chainguard).toMatch(/apk add[^\n]*libseccomp/);
});
it("Android backup of app data is disabled", () => {
const src = readSource("android/app/src/main/AndroidManifest.xml");
expect(src).toMatch(/android:allowBackup\s*=\s*"false"/);

View file

@ -104,6 +104,41 @@ describe("TelephonySettingsSection", () => {
});
describe("AppearanceSettingsSection", () => {
it("renders glass and split-view toggles as stacked setting rows", () => {
const wrapper = mount(AppearanceSettingsSection, {
props: {
visible: true,
config: {
theme: "dark",
messages_sidebar_position: "left",
message_font_size: 14,
message_icon_size: 28,
ui_transparency: 0,
ui_glass_enabled: true,
messages_multi_pane_enabled: true,
nomad_tabs_enabled: true,
rrc_enabled: true,
rrc_unread_badges_enabled: true,
},
detailedOutboundSendStatus: false,
messageIconPreviewStyle: { width: "28px", height: "28px" },
},
global: {
mocks: { $t: (key) => key },
stubs: {
Toggle: {
template: '<button type="button" class="toggle-stub" />',
},
MaterialDesignIcon: true,
},
},
});
const toggles = wrapper.findAll("label.setting-toggle");
expect(toggles.length).toBeGreaterThanOrEqual(4);
expect(toggles[0].find(".setting-toggle__title").text()).toContain("ui_glass_enabled");
expect(toggles[0].find(".setting-toggle__description").text()).toContain("ui_glass_enabled_description");
});
it("emits theme and field events without mutating config prop", async () => {
const config = {
theme: "light",