UI: новый общий стиль, тёмная/светлая тема, настраиваемая палитра

- styles/main.css: роли цветов (по умолчанию «Индиго»), шкала отступов/скруглений/шрифтов,
  цвета отношений для обеих тем; жёсткие цвета в стилях заменены на роли.
- Палитра: пресеты и личные правки, «Оформление» Авто/День/Ночь, долгое нажатие —
  редактор цветов с экспортом/импортом.
- Каналы: пузыри постов автора, плашки дней, строка «Написать в канал…», «О канале»,
  создание канала с адресом из названия; лента открывается на свежих постах.
- Чаты: плоский список, чипы-фильтры, пузыри, плашки дней; нижняя панель скрыта в переписке.
- Корневые разделы — единая шапка; профиль и чужой профиль — общая карточка;
  настройки, кошелёк, сеансы — меню-списки.
- Нижняя панель: иконки без подписей, бейджи на иконках.
- confirmDialog вместо window.confirm/alert; на телефоне диалоги — шторки снизу.
- docs/UI-Design/ISSUES-for-dev.md — найденные ошибки сервера/UI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
q
2026-09-26 10:05:26 +03:00
co-authored by Claude Opus 5.5
parent 6e5b57fd7c
commit f8900e531a
94 changed files with 3337 additions and 2432 deletions
+52
View File
@@ -28,6 +28,56 @@ function pickUnit(seconds) {
return ['year', Math.round(years)];
}
// Время поста внутри дня: «14:32». День показывается отдельной плашкой.
export function formatClockTime(timestampMs) {
const ts = toNumber(timestampMs);
if (!ts) return '';
return new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }).format(new Date(ts));
}
// Плашка дня в ленте: «Сегодня», «Вчера», «24 сентября», «24 сентября 2025».
export function formatDayLabel(timestampMs) {
const ts = toNumber(timestampMs);
if (!ts) return '';
const dt = new Date(ts);
const today = new Date();
const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
const diffDays = Math.round((startOf(today) - startOf(dt)) / 86400000);
if (diffDays === 0) return 'Сегодня';
if (diffDays === 1) return 'Вчера';
const sameYear = dt.getFullYear() === today.getFullYear();
return new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long', ...(sameYear ? {} : { year: 'numeric' }) }).format(dt);
}
export function dayKey(timestampMs) {
const ts = toNumber(timestampMs);
if (!ts) return '';
const dt = new Date(ts);
return `${dt.getFullYear()}-${dt.getMonth()}-${dt.getDate()}`;
}
// Время в строках списков (чаты, каналы) — коротко, как в мессенджерах:
// сегодня «21:41», вчера «вчера», на этой неделе «пт», в этом году «24.09», раньше «24.09.25».
export function formatListTime(timestampMs) {
const ts = toNumber(timestampMs);
if (!ts) return '';
const dt = new Date(ts);
const now = new Date();
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
if (ts >= startOfToday) {
return new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }).format(dt);
}
const dayMs = 86400000;
if (ts >= startOfToday - dayMs) return 'вчера';
if (ts >= startOfToday - 6 * dayMs) {
return new Intl.DateTimeFormat('ru-RU', { weekday: 'short' }).format(dt);
}
if (dt.getFullYear() === now.getFullYear()) {
return new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit' }).format(dt);
}
return new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: '2-digit' }).format(dt);
}
export function formatRelativeTime(timestampMs) {
const ts = toNumber(timestampMs);
if (!ts) return '—';
@@ -39,6 +89,8 @@ export function formatRelativeTime(timestampMs) {
const ageSeconds = Math.max(0, (now - ts) / 1000);
const ageDays = ageSeconds / 86400;
if (ageSeconds < 60) return 'только что';
if (ageDays < 7) {
const [unit, value] = pickUnit(diffSeconds);
if (rtf) return rtf.format(value, unit);