SHA256
Compare commits
7
Commits
c03d94105c
...
52a9f1ac1d
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
52a9f1ac1d | ||
|
|
3dec5e3225 | ||
|
|
90ded1612a | ||
|
|
7ab9fb7c28 | ||
|
|
6f499c68e2 | ||
|
|
19beba8fd7 | ||
|
|
f7e33802c8 |
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
client.version=1.10.3
|
client.version=1.11.1
|
||||||
server.version=1.8.2
|
server.version=1.9.0
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
<link rel="apple-touch-icon" href="./img/logo.jpg" />
|
<link rel="apple-touch-icon" href="./img/logo.jpg" />
|
||||||
<title>СИЯНИЕ</title>
|
<title>СИЯНИЕ</title>
|
||||||
<script>
|
<script>
|
||||||
window.__SHINE_BUILD_HASH__ = '20260826145335';
|
window.__SHINE_BUILD_HASH__ = '20260901134200';
|
||||||
window.__SHINE_CLIENT_VERSION__ = '1.2.10';
|
window.__SHINE_CLIENT_VERSION__ = '1.2.10';
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { isTextToSpeechConfigured, speakTextBySettings } from '../services/speec
|
|||||||
import { showToast } from '../services/channels-ux.js';
|
import { showToast } from '../services/channels-ux.js';
|
||||||
import { buildDmReplyTechBlock, parseDmTechBlocks, sanitizeUserDmTextForSend } from '../services/dm-tech-blocks.js';
|
import { buildDmReplyTechBlock, parseDmTechBlocks, sanitizeUserDmTextForSend } from '../services/dm-tech-blocks.js';
|
||||||
import { loadProfileSnapshot } from '../services/user-profile-params.js';
|
import { loadProfileSnapshot } from '../services/user-profile-params.js';
|
||||||
|
import { userDisplayName } from '../services/user-display.js';
|
||||||
import { makeProfileLinksRoute, makeProfileRoute } from '../services/shine-routes.js';
|
import { makeProfileLinksRoute, makeProfileRoute } from '../services/shine-routes.js';
|
||||||
|
|
||||||
export const pageMeta = { id: 'chat-view', title: 'Чат' };
|
export const pageMeta = { id: 'chat-view', title: 'Чат' };
|
||||||
@@ -127,7 +128,7 @@ function createChatHeaderParts(login, navigate) {
|
|||||||
loginEl.setAttribute('role', 'heading');
|
loginEl.setAttribute('role', 'heading');
|
||||||
loginEl.setAttribute('aria-level', '1');
|
loginEl.setAttribute('aria-level', '1');
|
||||||
loginEl.setAttribute('aria-label', `Чат с ${cleanLogin}`);
|
loginEl.setAttribute('aria-label', `Чат с ${cleanLogin}`);
|
||||||
loginEl.textContent = cleanLogin;
|
loginEl.innerHTML = `<span class="chat-header-display-name">${cleanLogin}</span><span class="chat-header-user-login">${cleanLogin}</span>`;
|
||||||
|
|
||||||
void loadProfileSnapshot(cleanLogin)
|
void loadProfileSnapshot(cleanLogin)
|
||||||
.then((snapshot) => {
|
.then((snapshot) => {
|
||||||
@@ -147,6 +148,13 @@ function createChatHeaderParts(login, navigate) {
|
|||||||
title: cleanLogin,
|
title: cleanLogin,
|
||||||
});
|
});
|
||||||
avatarSlot.replaceChildren(upgradedAvatar);
|
avatarSlot.replaceChildren(upgradedAvatar);
|
||||||
|
if (loginEl.isConnected) {
|
||||||
|
const display = userDisplayName({ login: cleanLogin, firstName: snapshot?.firstName, lastName: snapshot?.lastName });
|
||||||
|
const nameNode = loginEl.querySelector('.chat-header-display-name');
|
||||||
|
const loginNode = loginEl.querySelector('.chat-header-user-login');
|
||||||
|
if (nameNode) nameNode.textContent = display;
|
||||||
|
if (loginNode) loginNode.textContent = cleanLogin;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import { renderUserAvatar } from '../components/avatar-image.js';
|
|||||||
import { createOverflowDots } from '../components/overflow-dots.js';
|
import { createOverflowDots } from '../components/overflow-dots.js';
|
||||||
import { createDropdownMenu } from '../components/dropdown-menu.js';
|
import { createDropdownMenu } from '../components/dropdown-menu.js';
|
||||||
import { createShineConnectionsLogo } from '../components/shine-logo.js';
|
import { createShineConnectionsLogo } from '../components/shine-logo.js';
|
||||||
import { loadProfileSnapshot } from '../services/user-profile-params.js';
|
|
||||||
import { parseDmTechBlocks } from '../services/dm-tech-blocks.js';
|
import { parseDmTechBlocks } from '../services/dm-tech-blocks.js';
|
||||||
import { formatRelativeTime } from '../services/channels-ux.js';
|
import { formatRelativeTime } from '../services/channels-ux.js';
|
||||||
|
import { userDisplayName } from '../services/user-display.js';
|
||||||
|
|
||||||
export const pageMeta = { id: 'messages-list', title: 'Личные сообщения' };
|
export const pageMeta = { id: 'messages-list', title: 'Личные сообщения' };
|
||||||
const PREVIEW_MAX_LEN = 200;
|
const PREVIEW_MAX_LEN = 200;
|
||||||
@@ -24,69 +24,9 @@ const SVG_CHEVRON = `
|
|||||||
`;
|
`;
|
||||||
const DM_BLOB_PREVIEW_CACHE = new Map();
|
const DM_BLOB_PREVIEW_CACHE = new Map();
|
||||||
const DM_BLOB_PREVIEW_PENDING = new Map();
|
const DM_BLOB_PREVIEW_PENDING = new Map();
|
||||||
const dmAvatarSnapshotCache = new Map();
|
|
||||||
const dmAvatarPendingByLogin = new Map();
|
|
||||||
|
|
||||||
const RELATION_ORDER = new Map([
|
|
||||||
['close_friend', 0],
|
|
||||||
['contact', 1],
|
|
||||||
['none', 2],
|
|
||||||
]);
|
|
||||||
|
|
||||||
async function loadDmAvatarSnapshot(login) {
|
|
||||||
const cleanLogin = String(login || '').trim();
|
|
||||||
if (!cleanLogin) return null;
|
|
||||||
const key = cleanLogin.toLowerCase();
|
|
||||||
if (dmAvatarSnapshotCache.has(key)) return dmAvatarSnapshotCache.get(key);
|
|
||||||
if (dmAvatarPendingByLogin.has(key)) return dmAvatarPendingByLogin.get(key);
|
|
||||||
const pending = loadProfileSnapshot(cleanLogin)
|
|
||||||
.then((snapshot) => {
|
|
||||||
dmAvatarSnapshotCache.set(key, snapshot || null);
|
|
||||||
dmAvatarPendingByLogin.delete(key);
|
|
||||||
return snapshot || null;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
dmAvatarSnapshotCache.set(key, null);
|
|
||||||
dmAvatarPendingByLogin.delete(key);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
dmAvatarPendingByLogin.set(key, pending);
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDmAvatar(login, { className = '' } = {}) {
|
|
||||||
const cleanLogin = String(login || '').trim();
|
|
||||||
const title = cleanLogin ? `Профиль ${cleanLogin}` : '';
|
|
||||||
const avatarEl = renderUserAvatar({
|
|
||||||
login: cleanLogin || 'unknown',
|
|
||||||
size: 'lg',
|
|
||||||
title,
|
|
||||||
className,
|
|
||||||
});
|
|
||||||
if (!cleanLogin) return avatarEl;
|
|
||||||
void loadDmAvatarSnapshot(cleanLogin).then((snapshot) => {
|
|
||||||
if (!avatarEl.isConnected) return;
|
|
||||||
const upgraded = renderUserAvatar({
|
|
||||||
login: cleanLogin,
|
|
||||||
avatar: snapshot?.avatar?.txId
|
|
||||||
? {
|
|
||||||
ar: String(snapshot.avatar.txId || '').trim(),
|
|
||||||
sha256Hex: String(snapshot?.avatar?.sha256Hex || '').trim().toLowerCase(),
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
size: 'lg',
|
|
||||||
title,
|
|
||||||
className,
|
|
||||||
});
|
|
||||||
upgraded.classList.add('avatar');
|
|
||||||
avatarEl.replaceWith(upgraded);
|
|
||||||
});
|
|
||||||
return avatarEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeRelationFlag(value) {
|
function normalizeRelationFlag(value) {
|
||||||
const clean = String(value || '').trim().toLowerCase();
|
const clean = String(value || '').trim().toLowerCase();
|
||||||
if (clean === 'close_friend' || clean === 'contact') return clean;
|
if (clean === 'close_friend' || clean === 'friend' || clean === 'contact') return clean;
|
||||||
return 'none';
|
return 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +38,8 @@ function relationLabel(flag) {
|
|||||||
switch (normalizeRelationFlag(flag)) {
|
switch (normalizeRelationFlag(flag)) {
|
||||||
case 'close_friend':
|
case 'close_friend':
|
||||||
return 'близкий друг';
|
return 'близкий друг';
|
||||||
|
case 'friend':
|
||||||
|
return 'друг';
|
||||||
case 'contact':
|
case 'contact':
|
||||||
return 'контакт';
|
return 'контакт';
|
||||||
default:
|
default:
|
||||||
@@ -225,6 +167,7 @@ export function render({ navigate, chrome }) {
|
|||||||
const filterLabels = {
|
const filterLabels = {
|
||||||
all: 'Чаты',
|
all: 'Чаты',
|
||||||
close_friend: 'Близкие друзья',
|
close_friend: 'Близкие друзья',
|
||||||
|
friend: 'Друзья',
|
||||||
contact: 'Контакты',
|
contact: 'Контакты',
|
||||||
none: 'Новые',
|
none: 'Новые',
|
||||||
};
|
};
|
||||||
@@ -237,6 +180,7 @@ export function render({ navigate, chrome }) {
|
|||||||
items: [
|
items: [
|
||||||
{ label: 'Все чаты', action: () => { currentChatFilter = 'all'; filterTitle.textContent = filterLabels.all; reloadForFilter(); } },
|
{ label: 'Все чаты', action: () => { currentChatFilter = 'all'; filterTitle.textContent = filterLabels.all; reloadForFilter(); } },
|
||||||
{ label: 'Близкие друзья', action: () => { currentChatFilter = 'close_friend'; filterTitle.textContent = filterLabels.close_friend; reloadForFilter(); } },
|
{ label: 'Близкие друзья', action: () => { currentChatFilter = 'close_friend'; filterTitle.textContent = filterLabels.close_friend; reloadForFilter(); } },
|
||||||
|
{ label: 'Друзья', action: () => { currentChatFilter = 'friend'; filterTitle.textContent = filterLabels.friend; reloadForFilter(); } },
|
||||||
{ label: 'Контакты', action: () => { currentChatFilter = 'contact'; filterTitle.textContent = filterLabels.contact; reloadForFilter(); } },
|
{ label: 'Контакты', action: () => { currentChatFilter = 'contact'; filterTitle.textContent = filterLabels.contact; reloadForFilter(); } },
|
||||||
{ label: 'Новые', action: () => { currentChatFilter = 'none'; filterTitle.textContent = filterLabels.none; reloadForFilter(); } },
|
{ label: 'Новые', action: () => { currentChatFilter = 'none'; filterTitle.textContent = filterLabels.none; reloadForFilter(); } },
|
||||||
],
|
],
|
||||||
@@ -338,7 +282,14 @@ function renderRow(item) {
|
|||||||
const relationBadge = relationFlag === 'none'
|
const relationBadge = relationFlag === 'none'
|
||||||
? 'не в контактах'
|
? 'не в контактах'
|
||||||
: relationLabel(relationFlag);
|
: relationLabel(relationFlag);
|
||||||
const avatarEl = createDmAvatar(item.peerLogin);
|
const avatarEl = renderUserAvatar({
|
||||||
|
login: item.peerLogin,
|
||||||
|
firstName: item.firstName,
|
||||||
|
lastName: item.lastName,
|
||||||
|
avatar: item.avatarAr ? { ar: String(item.avatarAr).trim() } : null,
|
||||||
|
size: 'lg',
|
||||||
|
title: `Профиль ${item.peerLogin}`,
|
||||||
|
});
|
||||||
avatarEl.classList.add('avatar');
|
avatarEl.classList.add('avatar');
|
||||||
const avatarWrap = document.createElement('div');
|
const avatarWrap = document.createElement('div');
|
||||||
avatarWrap.className = 'dm-av dm-av--default';
|
avatarWrap.className = 'dm-av dm-av--default';
|
||||||
@@ -362,7 +313,7 @@ function renderRow(item) {
|
|||||||
const titleEl = row.querySelector('.dm-row-title');
|
const titleEl = row.querySelector('.dm-row-title');
|
||||||
const previewEl = row.querySelector('.dm-row-last-message');
|
const previewEl = row.querySelector('.dm-row-last-message');
|
||||||
const timeEl = row.querySelector('.dm-row-time');
|
const timeEl = row.querySelector('.dm-row-time');
|
||||||
if (titleEl) titleEl.textContent = String(item.peerLogin || '');
|
if (titleEl) titleEl.textContent = userDisplayName(item);
|
||||||
if (previewEl) previewEl.textContent = 'Загрузка…';
|
if (previewEl) previewEl.textContent = 'Загрузка…';
|
||||||
if (timeEl) timeEl.textContent = formatChatRowTime(item.lastMessageTimeMs);
|
if (timeEl) timeEl.textContent = formatChatRowTime(item.lastMessageTimeMs);
|
||||||
row.prepend(avatarWrap);
|
row.prepend(avatarWrap);
|
||||||
@@ -399,6 +350,11 @@ function renderRow(item) {
|
|||||||
const next = {
|
const next = {
|
||||||
id: peerLogin,
|
id: peerLogin,
|
||||||
peerLogin,
|
peerLogin,
|
||||||
|
firstName: String(dialog?.firstName || '').trim(),
|
||||||
|
lastName: String(dialog?.lastName || '').trim(),
|
||||||
|
avatarAr: String(dialog?.avatarAr || '').trim(),
|
||||||
|
accountRole: String(dialog?.accountRole || '').trim(),
|
||||||
|
shineStatus: String(dialog?.shineStatus || '').trim(),
|
||||||
relationFlag,
|
relationFlag,
|
||||||
lastMessageBlobB64: String(dialog?.lastMessageBlobB64 || ''),
|
lastMessageBlobB64: String(dialog?.lastMessageBlobB64 || ''),
|
||||||
lastMessageTimeMs: Number(dialog?.lastMessageTimeMs || 0),
|
lastMessageTimeMs: Number(dialog?.lastMessageTimeMs || 0),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { makeProfileLinksRoute } from '../services/shine-routes.js';
|
|||||||
import { createForceGraph } from './network/force-graph.js';
|
import { createForceGraph } from './network/force-graph.js';
|
||||||
import { engineModelFromGraphModel } from './network/adapter.js';
|
import { engineModelFromGraphModel } from './network/adapter.js';
|
||||||
import { openNodeMenu } from './network/node-menu.js';
|
import { openNodeMenu } from './network/node-menu.js';
|
||||||
|
import { userDisplayName } from '../services/user-display.js';
|
||||||
|
|
||||||
export const pageMeta = { id: 'network-view', title: 'Связи' };
|
export const pageMeta = { id: 'network-view', title: 'Связи' };
|
||||||
|
|
||||||
@@ -24,6 +25,32 @@ function createDebounced(fn, delayMs = 2000) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createHeaderSearchIcon() {
|
||||||
|
const ns = 'http://www.w3.org/2000/svg';
|
||||||
|
const svg = document.createElementNS(ns, 'svg');
|
||||||
|
svg.setAttribute('viewBox', '0 0 24 24');
|
||||||
|
svg.setAttribute('aria-hidden', 'true');
|
||||||
|
svg.setAttribute('class', 'header-icon-svg header-icon-svg--search');
|
||||||
|
|
||||||
|
const circle = document.createElementNS(ns, 'circle');
|
||||||
|
circle.setAttribute('cx', '11');
|
||||||
|
circle.setAttribute('cy', '11');
|
||||||
|
circle.setAttribute('r', '6.5');
|
||||||
|
circle.setAttribute('fill', 'none');
|
||||||
|
circle.setAttribute('stroke', 'currentColor');
|
||||||
|
circle.setAttribute('stroke-width', '2');
|
||||||
|
|
||||||
|
const handle = document.createElementNS(ns, 'path');
|
||||||
|
handle.setAttribute('d', 'M16 16l4.5 4.5');
|
||||||
|
handle.setAttribute('fill', 'none');
|
||||||
|
handle.setAttribute('stroke', 'currentColor');
|
||||||
|
handle.setAttribute('stroke-width', '2');
|
||||||
|
handle.setAttribute('stroke-linecap', 'round');
|
||||||
|
|
||||||
|
svg.append(circle, handle);
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
function normKey(value) {
|
function normKey(value) {
|
||||||
return normalizeLogin(value).toLowerCase();
|
return normalizeLogin(value).toLowerCase();
|
||||||
}
|
}
|
||||||
@@ -73,6 +100,12 @@ function getMarkByLogin(allUsers) {
|
|||||||
if (!login) return;
|
if (!login) return;
|
||||||
map.set(normKey(login), {
|
map.set(normKey(login), {
|
||||||
login,
|
login,
|
||||||
|
firstName: String(row?.firstName || '').trim(),
|
||||||
|
lastName: String(row?.lastName || '').trim(),
|
||||||
|
displayName: userDisplayName({ login, firstName: row?.firstName, lastName: row?.lastName }),
|
||||||
|
relationType: String(row?.relationType || '').trim().toLowerCase(),
|
||||||
|
primaryConfirmed: Boolean(row?.primaryConfirmed),
|
||||||
|
shineConfirmed: Boolean(row?.shineConfirmed),
|
||||||
official: Boolean(row?.official),
|
official: Boolean(row?.official),
|
||||||
shine: Boolean(row?.shine),
|
shine: Boolean(row?.shine),
|
||||||
officialLabel: String(row?.officialLabel || (row?.official ? 'официальный' : 'неофициальный')),
|
officialLabel: String(row?.officialLabel || (row?.official ? 'официальный' : 'неофициальный')),
|
||||||
@@ -210,7 +243,7 @@ export function render({ navigate, route, chrome } = {}) {
|
|||||||
// Фильтры слоёв (Фаза 3). Фокус всегда виден; предикат применяется к периферийным узлам.
|
// Фильтры слоёв (Фаза 3). Фокус всегда виден; предикат применяется к периферийным узлам.
|
||||||
const FILTERS = {
|
const FILTERS = {
|
||||||
all: { label: 'Все', pred: () => true },
|
all: { label: 'Все', pred: () => true },
|
||||||
friends: { label: 'Друзья', pred: (n) => n.relationType === 'friend' },
|
friends: { label: 'Друзья', pred: (n) => n.relationType === 'friend' || n.relationType === 'close_friend' },
|
||||||
shining: { label: 'Сияющие', pred: (n) => Boolean(n.shining) },
|
shining: { label: 'Сияющие', pred: (n) => Boolean(n.shining) },
|
||||||
};
|
};
|
||||||
const FILTER_ORDER = ['all', 'friends', 'shining'];
|
const FILTER_ORDER = ['all', 'friends', 'shining'];
|
||||||
@@ -386,6 +419,7 @@ export function render({ navigate, route, chrome } = {}) {
|
|||||||
const login = normalizeLogin(node.login);
|
const login = normalizeLogin(node.login);
|
||||||
openNodeMenu({
|
openNodeMenu({
|
||||||
login,
|
login,
|
||||||
|
displayName: String(node.name || '').trim(),
|
||||||
relationType: node.relationType,
|
relationType: node.relationType,
|
||||||
point,
|
point,
|
||||||
actions: [
|
actions: [
|
||||||
@@ -427,12 +461,15 @@ export function render({ navigate, route, chrome } = {}) {
|
|||||||
const header = renderHeader({
|
const header = renderHeader({
|
||||||
title: 'Связи',
|
title: 'Связи',
|
||||||
rightActions: [
|
rightActions: [
|
||||||
{ label: 'Найти', onClick: openSearchModal },
|
{
|
||||||
|
iconNode: createHeaderSearchIcon(),
|
||||||
|
title: 'Найти пользователя',
|
||||||
|
ariaLabel: 'Найти пользователя',
|
||||||
|
className: 'chat-header-icon-btn',
|
||||||
|
onClick: openSearchModal,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
// «Связи» используют тот же общий topbar, что и остальные страницы.
|
|
||||||
// Отдельный класс нужен только для page-specific fade графа, не для геометрии header.
|
|
||||||
header.classList.add('network-topbar');
|
|
||||||
|
|
||||||
// Ресайз и перерисовку рёбер движок обрабатывает сам (window resize + ResizeObserver внутри).
|
// Ресайз и перерисовку рёбер движок обрабатывает сам (window resize + ResizeObserver внутри).
|
||||||
screen.cleanup = () => {
|
screen.cleanup = () => {
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ function normLogin(value) {
|
|||||||
|
|
||||||
const FAMILY_ROLES = ['parent', 'child', 'spouse', 'sibling'];
|
const FAMILY_ROLES = ['parent', 'child', 'spouse', 'sibling'];
|
||||||
|
|
||||||
function relationTypeFromRole(role) {
|
function relationTypeFromRelation(relation) {
|
||||||
|
const explicit = String(relation?.mark?.relationType || '').trim().toLowerCase();
|
||||||
|
if (explicit === 'close_friend' || explicit === 'friend' || explicit === 'contact') return explicit;
|
||||||
|
const role = relation?.role;
|
||||||
if (FAMILY_ROLES.includes(role)) return 'family';
|
if (FAMILY_ROLES.includes(role)) return 'family';
|
||||||
if (role === 'friend') return 'friend';
|
if (role === 'friend') return 'friend';
|
||||||
return 'contact';
|
return 'contact';
|
||||||
@@ -22,6 +25,7 @@ function relationTypeFromRole(role) {
|
|||||||
// - взаимные дружеские связи ближе односторонних.
|
// - взаимные дружеские связи ближе односторонних.
|
||||||
function deriveStrength(relation) {
|
function deriveStrength(relation) {
|
||||||
if (relation.isRelative) return 0.9;
|
if (relation.isRelative) return 0.9;
|
||||||
|
if (relation?.mark?.relationType === 'close_friend') return 0.9;
|
||||||
if (relation.role === 'contact') return 0.4;
|
if (relation.role === 'contact') return 0.4;
|
||||||
if (relation.forward && relation.backward) return 0.8;
|
if (relation.forward && relation.backward) return 0.8;
|
||||||
return 0.55;
|
return 0.55;
|
||||||
@@ -38,7 +42,7 @@ export function engineModelFromGraphModel(graphModel) {
|
|||||||
const focusNode = {
|
const focusNode = {
|
||||||
id: focusLogin,
|
id: focusLogin,
|
||||||
login: focusLogin,
|
login: focusLogin,
|
||||||
name: '',
|
name: centerMark?.displayName || centerMark?.login || focusLogin,
|
||||||
avatar: centerMark?.avatar || null,
|
avatar: centerMark?.avatar || null,
|
||||||
relationType: 'self',
|
relationType: 'self',
|
||||||
strength: 1,
|
strength: 1,
|
||||||
@@ -58,9 +62,9 @@ export function engineModelFromGraphModel(graphModel) {
|
|||||||
return {
|
return {
|
||||||
id: login,
|
id: login,
|
||||||
login,
|
login,
|
||||||
name: '',
|
name: r?.mark?.displayName || login,
|
||||||
avatar: r?.mark?.avatar || null,
|
avatar: r?.mark?.avatar || null,
|
||||||
relationType: relationTypeFromRole(r?.role),
|
relationType: relationTypeFromRelation(r),
|
||||||
strength: deriveStrength(r || {}),
|
strength: deriveStrength(r || {}),
|
||||||
shining: Boolean(r?.mark?.shine),
|
shining: Boolean(r?.mark?.shine),
|
||||||
tier: 1,
|
tier: 1,
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ const DOUBLE_TAP_MS = 320; // окно двойного тапа по фо
|
|||||||
const RELATION_COLORS = {
|
const RELATION_COLORS = {
|
||||||
family: 'rgba(255, 159, 94, 0.92)',
|
family: 'rgba(255, 159, 94, 0.92)',
|
||||||
friend: 'rgba(120, 179, 255, 0.9)',
|
friend: 'rgba(120, 179, 255, 0.9)',
|
||||||
|
close_friend: 'rgba(120, 179, 255, 0.98)',
|
||||||
business: 'rgba(190, 150, 255, 0.9)',
|
business: 'rgba(190, 150, 255, 0.9)',
|
||||||
contact: 'rgba(170, 190, 220, 0.7)',
|
contact: 'rgba(170, 190, 220, 0.7)',
|
||||||
};
|
};
|
||||||
@@ -593,7 +594,7 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
|||||||
function updateA11y() {
|
function updateA11y() {
|
||||||
const focus = nodes.find((n) => n.isFocus);
|
const focus = nodes.find((n) => n.isFocus);
|
||||||
const tier1 = nodes.filter((n) => n.tier === 1 && !n.isFocus);
|
const tier1 = nodes.filter((n) => n.tier === 1 && !n.isFocus);
|
||||||
const rel = { family: 'семья', friend: 'друг', business: 'бизнес', contact: 'контакт' };
|
const rel = { family: 'семья', friend: 'друг', close_friend: 'близкий друг', business: 'бизнес', contact: 'контакт' };
|
||||||
const items = tier1.map((n) => `<li>${escapeHtml(n.name || n.login || String(n.id))}${n.shining ? ' — сияющий' : ''} (${rel[n.relationType] || 'связь'})</li>`).join('');
|
const items = tier1.map((n) => `<li>${escapeHtml(n.name || n.login || String(n.id))}${n.shining ? ' — сияющий' : ''} (${rel[n.relationType] || 'связь'})</li>`).join('');
|
||||||
a11y.innerHTML = `<p>Центр: ${escapeHtml(focus ? (focus.name || focus.login || '') : '')}. Связей 1-го уровня: ${tier1.length}.</p><ul>${items}</ul>`;
|
a11y.innerHTML = `<p>Центр: ${escapeHtml(focus ? (focus.name || focus.login || '') : '')}. Связей 1-го уровня: ${tier1.length}.</p><ul>${items}</ul>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function relationLabelRu(relationType) {
|
|||||||
* @param {{x:number,y:number,rect?:DOMRect}} opts.point - экранная точка/rect узла
|
* @param {{x:number,y:number,rect?:DOMRect}} opts.point - экранная точка/rect узла
|
||||||
* @param {Array<{label:string, onClick:Function, disabled?:boolean}>} opts.actions - пункты меню
|
* @param {Array<{label:string, onClick:Function, disabled?:boolean}>} opts.actions - пункты меню
|
||||||
*/
|
*/
|
||||||
export function openNodeMenu({ login, relationType, point, actions = [] } = {}) {
|
export function openNodeMenu({ login, displayName = '', relationType, point, actions = [] } = {}) {
|
||||||
const root = document.getElementById('modal-root');
|
const root = document.getElementById('modal-root');
|
||||||
if (!(root instanceof HTMLElement)) return;
|
if (!(root instanceof HTMLElement)) return;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ export function openNodeMenu({ login, relationType, point, actions = [] } = {})
|
|||||||
<div class="fg-menu-overlay" id="fg-menu-overlay">
|
<div class="fg-menu-overlay" id="fg-menu-overlay">
|
||||||
<div class="fg-menu" id="fg-menu" role="menu">
|
<div class="fg-menu" id="fg-menu" role="menu">
|
||||||
<div class="fg-menu-head">
|
<div class="fg-menu-head">
|
||||||
<span class="fg-menu-login">${escapeHtml(login)}</span>
|
<span class="fg-menu-login">${escapeHtml(displayName || login)}</span>${displayName && displayName !== login ? `<small class="fg-menu-secondary-login">${escapeHtml(login)}</small>` : ''}
|
||||||
<span class="fg-menu-rel">${escapeHtml(relationLabelRu(relationType))}</span>
|
<span class="fg-menu-rel">${escapeHtml(relationLabelRu(relationType))}</span>
|
||||||
</div>
|
</div>
|
||||||
${itemsHtml}
|
${itemsHtml}
|
||||||
|
|||||||
+134
-247
@@ -1,27 +1,15 @@
|
|||||||
import { profile } from '../mock-data.js';
|
import { profile } from '../mock-data.js';
|
||||||
import { authService, state } from '../state.js';
|
import { authService, state } from '../state.js';
|
||||||
import {
|
import {
|
||||||
PROFILE_GENDER_FEMALE,
|
|
||||||
PROFILE_GENDER_MALE,
|
|
||||||
loadProfileSnapshot,
|
loadProfileSnapshot,
|
||||||
} from '../services/user-profile-params.js';
|
} from '../services/user-profile-params.js';
|
||||||
import { buildIdentityLines } from '../services/user-connections.js';
|
|
||||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||||
import { createOverflowDots } from '../components/overflow-dots.js';
|
import { createOverflowDots } from '../components/overflow-dots.js';
|
||||||
import { createDropdownMenu } from '../components/dropdown-menu.js';
|
import { createDropdownMenu } from '../components/dropdown-menu.js';
|
||||||
|
import { userDisplayName } from '../services/user-display.js';
|
||||||
|
|
||||||
export const pageMeta = { id: 'profile-view', title: 'Профиль' };
|
export const pageMeta = { id: 'profile-view', title: 'Профиль' };
|
||||||
|
|
||||||
function toggleText(enabled) {
|
|
||||||
return enabled ? 'Yes' : 'No';
|
|
||||||
}
|
|
||||||
|
|
||||||
function genderLabel(value) {
|
|
||||||
if (value === PROFILE_GENDER_MALE) return 'Мужской';
|
|
||||||
if (value === PROFILE_GENDER_FEMALE) return 'Женский';
|
|
||||||
return 'Не указан';
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
return String(text || '')
|
return String(text || '')
|
||||||
.replaceAll('&', '&')
|
.replaceAll('&', '&')
|
||||||
@@ -31,72 +19,57 @@ function escapeHtml(text) {
|
|||||||
.replaceAll("'", ''');
|
.replaceAll("'", ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderProfileInfoText(text) {
|
function fieldMap(snapshot) {
|
||||||
const [intro, details] = String(text || '').split(/\n\n/, 2);
|
const out = {};
|
||||||
if (!details) {
|
(Array.isArray(snapshot?.fields) ? snapshot.fields : []).forEach((field) => {
|
||||||
return `<p class="profile-info-modal__lead">${escapeHtml(intro)}</p>`;
|
out[String(field?.key || '').trim()] = String(field?.value || '').trim();
|
||||||
|
});
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [sectionTitle, ...items] = details.split('\n').filter(Boolean);
|
function openTextModal(title, text) {
|
||||||
return `
|
|
||||||
<p class="profile-info-modal__lead">${escapeHtml(intro)}</p>
|
|
||||||
<section class="profile-info-modal__section" aria-label="${escapeHtml(sectionTitle)}">
|
|
||||||
<p class="profile-info-modal__section-title">${escapeHtml(sectionTitle)}</p>
|
|
||||||
<ol class="profile-info-modal__list">
|
|
||||||
${items.map((item) => `<li>${escapeHtml(item.replace(/^\d+\)\s*/, ''))}</li>`).join('')}
|
|
||||||
</ol>
|
|
||||||
</section>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openProfileInfoModal({ title, text }) {
|
|
||||||
const root = document.getElementById('modal-root');
|
const root = document.getElementById('modal-root');
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
root.innerHTML = `
|
root.innerHTML = `
|
||||||
<div class="modal profile-info-modal" id="profile-info-modal" role="dialog" aria-modal="true" aria-labelledby="profile-info-title">
|
<div class="modal" id="profile-text-modal">
|
||||||
<section class="modal-card profile-info-modal__card stack">
|
<div class="modal-card stack">
|
||||||
<header class="profile-info-modal__header">
|
<h3>${escapeHtml(title)}</h3>
|
||||||
<div>
|
<div style="white-space:pre-wrap;line-height:1.5">${escapeHtml(text || 'Не заполнено')}</div>
|
||||||
<p class="profile-info-modal__eyebrow">Информация</p>
|
<button class="secondary-btn" id="profile-text-close">Закрыть</button>
|
||||||
<h3 class="modal-title profile-info-modal__title" id="profile-info-title">${escapeHtml(title)}</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="profile-info-modal__close" type="button" id="profile-info-close-icon" aria-label="Закрыть" title="Закрыть">×</button>
|
</div>`;
|
||||||
</header>
|
|
||||||
<div class="profile-info-modal__content">
|
|
||||||
${renderProfileInfoText(text)}
|
|
||||||
</div>
|
|
||||||
<footer class="profile-info-modal__footer">
|
|
||||||
<button class="secondary-btn profile-info-modal__confirm" type="button" id="profile-info-close">Готово</button>
|
|
||||||
</footer>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
const close = () => { root.innerHTML = ''; };
|
const close = () => { root.innerHTML = ''; };
|
||||||
root.querySelector('#profile-info-close')?.addEventListener('click', close);
|
root.querySelector('#profile-text-close')?.addEventListener('click', close);
|
||||||
root.querySelector('#profile-info-close-icon')?.addEventListener('click', close);
|
root.querySelector('#profile-text-modal')?.addEventListener('click', (event) => {
|
||||||
root.querySelector('#profile-info-modal')?.addEventListener('click', (event) => {
|
if (event.target?.id === 'profile-text-modal') close();
|
||||||
if (event.target?.id === 'profile-info-modal') close();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function officialInfoText() {
|
function statusBadges(accountRole, shineStatus) {
|
||||||
return 'Можно создавать несколько альтернативных или анонимных каналов. '
|
const role = accountRole === 'primary' ? 'Основной аккаунт' : accountRole === 'non_voting' ? 'Голос не учитывать' : '';
|
||||||
+ 'Но для корректного учёта голосов на одного реального человека используется только один основной аккаунт.';
|
const shine = shineStatus === 'shining' ? 'Сияющий' : '';
|
||||||
|
return `<div class="row wrap-row">
|
||||||
|
${role ? `<span class="badge">${escapeHtml(role)}</span>` : ''}
|
||||||
|
${shine ? '<span class="badge is-yes-shine">Сияющий</span>' : ''}
|
||||||
|
${shineStatus === 'not_interested' ? '<span class="profile-shine-not-interested" title="Сияние неинтересно"><img src="/assets/shine-status-not-interested.svg" alt="Сияние неинтересно"></span>' : ''}
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shineInfoText() {
|
function statsRows(stats = {}) {
|
||||||
return 'Сияющие — это те, от кого идёт внутреннее сияние на тонком плане.\n\n'
|
return [
|
||||||
+ 'Пять принципов сияющих:\n'
|
['friends', 'Друзья', stats.friendsCount],
|
||||||
+ '1) сияющие не обманывают;\n'
|
['close_friends', 'Близкие друзья', stats.closeFriendsCount],
|
||||||
+ '2) сияющие чувствуют, что человек — это не только физическое тело, а нечто большее;\n'
|
['primary_received', 'Подтвердили основной аккаунт', stats.primaryReceivedCount],
|
||||||
+ '3) сияющие развиваются и в духовной, и в материальной плоскости;\n'
|
['primary_given', 'Подтверждённые аккаунты', stats.primaryGivenCount],
|
||||||
+ '4) у сияющих есть близкие друзья, с которыми им по-настоящему хорошо;\n'
|
['shine_received', 'Считают сияющим', stats.shineReceivedCount],
|
||||||
+ '5) сияющие заботятся о мире: о людях, гармонии и общем благе.';
|
['shine_given', 'Подтверждённые сияющие', stats.shineGivenCount],
|
||||||
|
['channels_following', 'Подписки на каналы', stats.followingChannelsCount],
|
||||||
|
['channels_owned', 'Каналы', stats.ownedPublicChannelsCount],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function render({ navigate, chrome }) {
|
export function render({ navigate, chrome }) {
|
||||||
const login = state.session.login || profile.login;
|
const login = String(state.session.login || profile.login || '').trim();
|
||||||
|
|
||||||
const screen = document.createElement('section');
|
const screen = document.createElement('section');
|
||||||
screen.className = 'stack profile-screen';
|
screen.className = 'stack profile-screen';
|
||||||
|
|
||||||
@@ -106,15 +79,12 @@ export function render({ navigate, chrome }) {
|
|||||||
<div class="header-left" aria-hidden="true"></div>
|
<div class="header-left" aria-hidden="true"></div>
|
||||||
<div class="header-center"><h1 class="page-title">Профиль</h1></div>
|
<div class="header-center"><h1 class="page-title">Профиль</h1></div>
|
||||||
<div class="header-actions profile-head-menu-wrap dm-head-menu-wrap">
|
<div class="header-actions profile-head-menu-wrap dm-head-menu-wrap">
|
||||||
<button type="button" class="dm-head-menu-btn profile-head-menu-btn" aria-label="Меню профиля" aria-haspopup="menu" aria-expanded="false">
|
<button type="button" class="dm-head-menu-btn profile-head-menu-btn" aria-label="Меню профиля" aria-haspopup="menu" aria-expanded="false"></button>
|
||||||
</button>
|
</div>`;
|
||||||
</div>
|
const menuButton = topbar.querySelector('.profile-head-menu-btn');
|
||||||
`;
|
menuButton?.append(createOverflowDots());
|
||||||
|
|
||||||
const profileMenuButton = topbar.querySelector('.profile-head-menu-btn');
|
|
||||||
profileMenuButton?.append(createOverflowDots());
|
|
||||||
const profileMenu = createDropdownMenu({
|
const profileMenu = createDropdownMenu({
|
||||||
anchorEl: profileMenuButton,
|
anchorEl: menuButton,
|
||||||
className: 'profile-head-menu',
|
className: 'profile-head-menu',
|
||||||
minWidth: 230,
|
minWidth: 230,
|
||||||
items: [
|
items: [
|
||||||
@@ -123,197 +93,114 @@ export function render({ navigate, chrome }) {
|
|||||||
{ label: 'Настройки', iconSrc: '/assets/profile-icon-settings.svg', action: () => navigate('settings-view') },
|
{ label: 'Настройки', iconSrc: '/assets/profile-icon-settings.svg', action: () => navigate('settings-view') },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome?.setTopbar(topbar);
|
chrome?.setTopbar(topbar);
|
||||||
|
|
||||||
const card = document.createElement('div');
|
const status = document.createElement('div');
|
||||||
card.className = 'card stack profile-main-card';
|
status.className = 'status-line';
|
||||||
|
status.textContent = 'Загрузка профиля...';
|
||||||
|
const body = document.createElement('div');
|
||||||
|
body.className = 'stack';
|
||||||
|
screen.append(status, body);
|
||||||
|
|
||||||
const topRow = document.createElement('div');
|
let current = null;
|
||||||
topRow.className = 'row';
|
|
||||||
topRow.innerHTML = `
|
|
||||||
<div class="row" style="gap:12px; align-items:center;">
|
|
||||||
<div data-profile-avatar-slot="true"></div>
|
|
||||||
<div class="profile-identity-lines" data-profile-identity="true">
|
|
||||||
<div class="profile-identity-line profile-identity-login">${String(login || '').trim() || 'unknown'}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const badgesRow = document.createElement('div');
|
function renderProfile() {
|
||||||
badgesRow.className = 'row';
|
if (!current) return;
|
||||||
badgesRow.innerHTML = `
|
const { snapshot, user } = current;
|
||||||
<button class="badge profile-toggle-btn is-no" type="button" data-status="account_role">Аккаунт: Не указано</button>
|
const fields = fieldMap(snapshot);
|
||||||
<button class="badge profile-toggle-btn is-no" type="button" data-status="shine">Сияние: Не указано</button>
|
const firstName = fields.first_name || '';
|
||||||
`;
|
const lastName = fields.last_name || '';
|
||||||
|
const displayName = userDisplayName({ login, firstName, lastName });
|
||||||
const listWrap = document.createElement('div');
|
const avatar = snapshot?.avatar?.txId
|
||||||
listWrap.className = 'stack profile-param-list';
|
? { ar: String(snapshot.avatar.txId).trim(), sha256Hex: String(snapshot?.avatar?.sha256Hex || '').trim().toLowerCase() }
|
||||||
|
: null;
|
||||||
const accountRoleBtn = badgesRow.querySelector('[data-status="account_role"]');
|
const stats = {
|
||||||
const shineBtn = badgesRow.querySelector('[data-status="shine"]');
|
ownedPublicChannelsCount: Number(user?.ownedPublicChannelsCount || 0),
|
||||||
const identityEl = topRow.querySelector('[data-profile-identity="true"]');
|
followingChannelsCount: Number(user?.followingChannelsCount || 0),
|
||||||
const avatarSlotEl = topRow.querySelector('[data-profile-avatar-slot="true"]');
|
friendsCount: Number(user?.friendsCount || 0),
|
||||||
|
closeFriendsCount: Number(user?.closeFriendsCount || 0),
|
||||||
let currentFields = [];
|
primaryReceivedCount: Number(user?.primaryConfirmationsReceivedCount || 0),
|
||||||
let currentAccountRole = '';
|
primaryGivenCount: Number(user?.primaryConfirmationsGivenCount || 0),
|
||||||
let currentShineStatus = '';
|
shineReceivedCount: Number(user?.shineConfirmationsReceivedCount || 0),
|
||||||
let currentGender = 'unknown';
|
shineGivenCount: Number(user?.shineConfirmationsGivenCount || 0),
|
||||||
let currentAvatar = { value: '', source: '', txId: '', sha256Hex: '', timeMs: 0 };
|
|
||||||
let currentStats = {
|
|
||||||
ownedPublicChannelsCount: 0,
|
|
||||||
followingUsersCount: 0,
|
|
||||||
followingChannelsCount: 0,
|
|
||||||
closeFriendsCount: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function syncIdentity() {
|
body.innerHTML = '';
|
||||||
if (!identityEl) return;
|
const identity = document.createElement('div');
|
||||||
const firstName = currentFields.find((field) => field.key === 'first_name')?.value || '';
|
identity.className = 'card row';
|
||||||
const lastName = currentFields.find((field) => field.key === 'last_name')?.value || '';
|
identity.style.gap = '12px';
|
||||||
const lines = buildIdentityLines({ login, firstName, lastName });
|
identity.style.alignItems = 'center';
|
||||||
identityEl.innerHTML = lines.map((line, idx) => (
|
identity.append(renderUserAvatar({ login, firstName, lastName, avatar, size: 'xl', className: 'profile-avatar' }));
|
||||||
`<div class="profile-identity-line${idx === lines.length - 1 ? ' profile-identity-login' : ''}">${escapeHtml(line)}</div>`
|
const identityText = document.createElement('div');
|
||||||
)).join('');
|
identityText.innerHTML = `<div class="profile-identity-line">${escapeHtml(displayName)}</div><div class="profile-identity-login">${escapeHtml(login)}</div>`;
|
||||||
|
identity.append(identityText);
|
||||||
|
body.append(identity);
|
||||||
|
|
||||||
|
const badges = document.createElement('div');
|
||||||
|
badges.innerHTML = statusBadges(String(snapshot?.accountRole || '').trim().toLowerCase(), String(snapshot?.shineStatus || '').trim().toLowerCase());
|
||||||
|
body.append(...badges.children);
|
||||||
|
|
||||||
|
if (fields.about) {
|
||||||
|
const about = document.createElement('div');
|
||||||
|
about.className = 'card profile-about';
|
||||||
|
about.style.whiteSpace = 'pre-wrap';
|
||||||
|
about.textContent = fields.about;
|
||||||
|
body.append(about);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAvatarUi() {
|
const statsGrid = document.createElement('div');
|
||||||
if (!(avatarSlotEl instanceof HTMLElement)) return;
|
statsGrid.className = 'profile-stats-grid';
|
||||||
const firstName = String(currentFields.find((field) => field.key === 'first_name')?.value || '').trim();
|
statsRows(stats).forEach(([kind, label, value]) => {
|
||||||
const lastName = String(currentFields.find((field) => field.key === 'last_name')?.value || '').trim();
|
const button = document.createElement('button');
|
||||||
avatarSlotEl.innerHTML = '';
|
button.type = 'button';
|
||||||
avatarSlotEl.append(renderUserAvatar({
|
button.className = 'card profile-stat-card';
|
||||||
login,
|
button.dataset.profileList = kind;
|
||||||
firstName,
|
button.innerHTML = `<b>${Number(value || 0)}</b><span>${escapeHtml(label)}</span>`;
|
||||||
lastName,
|
statsGrid.append(button);
|
||||||
avatar: currentAvatar?.txId
|
|
||||||
? { ar: currentAvatar.txId, sha256Hex: String(currentAvatar?.sha256Hex || '').trim().toLowerCase() }
|
|
||||||
: null,
|
|
||||||
size: 'xl',
|
|
||||||
className: 'profile-avatar',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateStatusesUi() {
|
|
||||||
if (accountRoleBtn) {
|
|
||||||
const label = currentAccountRole === 'primary' ? 'Основной аккаунт' : currentAccountRole === 'non_voting' ? 'Не учитывать мой голос' : 'Не указано';
|
|
||||||
accountRoleBtn.textContent = `Аккаунт: ${label}`;
|
|
||||||
accountRoleBtn.classList.remove('is-no', 'is-yes-official', 'is-yes-shine', 'is-not-interested');
|
|
||||||
accountRoleBtn.classList.add(currentAccountRole === 'primary' ? 'is-yes-official' : 'is-no');
|
|
||||||
}
|
|
||||||
if (shineBtn) {
|
|
||||||
const label = currentShineStatus === 'shining' ? 'Сияющий' : currentShineStatus === 'not_interested' ? 'Сияние неинтересно' : currentShineStatus === 'unknown' ? 'Неизвестно' : 'Не указано';
|
|
||||||
shineBtn.textContent = `Сияние: ${label}`;
|
|
||||||
shineBtn.classList.remove('is-no', 'is-yes-official', 'is-yes-shine', 'is-not-interested');
|
|
||||||
shineBtn.classList.add(currentShineStatus === 'shining' ? 'is-yes-shine' : currentShineStatus === 'not_interested' ? 'is-not-interested' : 'is-no');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderStats() {
|
|
||||||
const stats = [
|
|
||||||
{ label: 'Собственные публичные каналы', value: currentStats.ownedPublicChannelsCount },
|
|
||||||
{ label: 'Подписки на пользователей', value: currentStats.followingUsersCount },
|
|
||||||
{ label: 'Подписки на каналы', value: currentStats.followingChannelsCount },
|
|
||||||
{ label: 'Близкие друзья', value: currentStats.closeFriendsCount },
|
|
||||||
];
|
|
||||||
stats.forEach((stat) => {
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'card profile-param-item row';
|
|
||||||
row.innerHTML = `<div class="profile-param-value"><b>${escapeHtml(stat.label)}</b>: ${escapeHtml(String(Number(stat.value || 0)))}</div>`;
|
|
||||||
listWrap.append(row);
|
|
||||||
});
|
});
|
||||||
|
body.append(statsGrid);
|
||||||
|
|
||||||
|
const detailRow = document.createElement('div');
|
||||||
|
detailRow.className = 'row wrap-row';
|
||||||
|
detailRow.innerHTML = '<button class="secondary-btn" data-profile-detail="contacts">Контакты</button><button class="secondary-btn" data-profile-detail="spiritual">Духовный путь</button>';
|
||||||
|
body.append(detailRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
accountRoleBtn?.classList.add('profile-badge-trigger');
|
body.addEventListener('click', (event) => {
|
||||||
shineBtn?.classList.add('profile-badge-trigger');
|
if (!current) return;
|
||||||
accountRoleBtn?.addEventListener('click', () => {
|
const el = event.target.closest('[data-profile-list],[data-profile-detail]');
|
||||||
openProfileInfoModal({
|
if (!el) return;
|
||||||
title: 'Основной аккаунт',
|
const kind = el.dataset.profileList;
|
||||||
text: officialInfoText(),
|
if (kind) {
|
||||||
});
|
navigate(`SHiNE/${encodeURIComponent(login)}/list/${encodeURIComponent(kind)}`);
|
||||||
});
|
|
||||||
shineBtn?.addEventListener('click', () => {
|
|
||||||
openProfileInfoModal({
|
|
||||||
title: 'Справка о сияющих',
|
|
||||||
text: shineInfoText(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function renderFields(fields) {
|
|
||||||
listWrap.innerHTML = '';
|
|
||||||
renderStats();
|
|
||||||
fields.forEach((field) => {
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'card profile-param-item row';
|
|
||||||
const value = String(field.value || '').trim() || 'не заполнено';
|
|
||||||
row.innerHTML = `<div class="profile-param-value"><b>${field.label}</b>: ${escapeHtml(value)}</div>`;
|
|
||||||
listWrap.append(row);
|
|
||||||
|
|
||||||
if (field.key === 'last_name') {
|
|
||||||
const genderRow = document.createElement('div');
|
|
||||||
genderRow.className = 'card profile-param-item row';
|
|
||||||
genderRow.innerHTML = `<div class="profile-param-value"><b>Пол</b>: ${escapeHtml(genderLabel(currentGender))}</div>`;
|
|
||||||
listWrap.append(genderRow);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshProfileSnapshot() {
|
|
||||||
if (state.session.isLocalDemo) {
|
|
||||||
currentFields = [
|
|
||||||
{ key: 'first_name', label: 'Имя', value: 'Тестовый' },
|
|
||||||
{ key: 'last_name', label: 'Фамилия', value: 'Пользователь' },
|
|
||||||
{ key: 'address', label: 'Адрес', value: 'Локальный режим' },
|
|
||||||
{ key: 'website', label: 'Веб', value: '127.0.0.1' },
|
|
||||||
{ key: 'phone', label: 'Телефон', value: profile.phone },
|
|
||||||
];
|
|
||||||
currentAccountRole = '';
|
|
||||||
currentShineStatus = '';
|
|
||||||
currentGender = 'unknown';
|
|
||||||
currentAvatar = { value: '', source: '', txId: '', sha256Hex: '', timeMs: 0 };
|
|
||||||
currentStats = {
|
|
||||||
ownedPublicChannelsCount: 0,
|
|
||||||
followingUsersCount: 0,
|
|
||||||
followingChannelsCount: 0,
|
|
||||||
closeFriendsCount: 0,
|
|
||||||
};
|
|
||||||
syncIdentity();
|
|
||||||
updateAvatarUi();
|
|
||||||
updateStatusesUi();
|
|
||||||
renderFields(currentFields);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const fields = fieldMap(current.snapshot);
|
||||||
try {
|
if (el.dataset.profileDetail === 'contacts') {
|
||||||
const [snapshot, user] = await Promise.all([
|
openTextModal('Контакты', [
|
||||||
loadProfileSnapshot(login),
|
fields.web ? `Links: ${fields.web}` : '',
|
||||||
authService.getUser(login).catch(() => ({})),
|
fields.phone ? `Телефон: ${fields.phone}` : '',
|
||||||
]);
|
fields.address ? `Адрес: ${fields.address}` : '',
|
||||||
currentFields = Array.isArray(snapshot.fields) ? snapshot.fields : [];
|
].filter(Boolean).join('\n') || 'Не заполнено');
|
||||||
currentAccountRole = snapshot.accountRole || '';
|
|
||||||
currentShineStatus = snapshot.shineStatus || '';
|
|
||||||
currentGender = snapshot.gender || 'unknown';
|
|
||||||
currentAvatar = snapshot.avatar || { value: '', source: '', txId: '', sha256Hex: '', timeMs: 0 };
|
|
||||||
currentStats = {
|
|
||||||
ownedPublicChannelsCount: Number(user?.ownedPublicChannelsCount || 0),
|
|
||||||
followingUsersCount: Number(user?.followingUsersCount || 0),
|
|
||||||
followingChannelsCount: Number(user?.followingChannelsCount || 0),
|
|
||||||
closeFriendsCount: Number(user?.closeFriendsCount || 0),
|
|
||||||
};
|
|
||||||
syncIdentity();
|
|
||||||
updateAvatarUi();
|
|
||||||
updateStatusesUi();
|
|
||||||
renderFields(currentFields);
|
|
||||||
} catch (error) {
|
|
||||||
// ignore status row in profile-view
|
|
||||||
}
|
}
|
||||||
|
if (el.dataset.profileDetail === 'spiritual') openTextModal('Духовный путь', fields.spiritual_path);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
if (state.session.isLocalDemo) {
|
||||||
|
status.textContent = 'Локальный тестовый режим.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [snapshot, user] = await Promise.all([loadProfileSnapshot(login), authService.getUser(login)]);
|
||||||
|
current = { snapshot, user };
|
||||||
|
renderProfile();
|
||||||
|
status.textContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
card.append(topRow, badgesRow, listWrap);
|
refresh().catch((error) => {
|
||||||
screen.append(card);
|
status.className = 'status-line is-unavailable';
|
||||||
|
status.textContent = `Ошибка: ${error?.message || 'unknown'}`;
|
||||||
updateAvatarUi();
|
});
|
||||||
refreshProfileSnapshot();
|
|
||||||
|
|
||||||
screen.cleanup = () => profileMenu.destroy();
|
screen.cleanup = () => profileMenu.destroy();
|
||||||
return screen;
|
return screen;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { renderHeader } from '../components/header.js';
|
|||||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||||
import { authService } from '../state.js';
|
import { authService } from '../state.js';
|
||||||
import { navigateBack } from '../router.js';
|
import { navigateBack } from '../router.js';
|
||||||
|
import { userDisplayName } from '../services/user-display.js';
|
||||||
|
|
||||||
export const pageMeta = { id: 'user-profile-list-view', title: 'Список' };
|
export const pageMeta = { id: 'user-profile-list-view', title: 'Список' };
|
||||||
|
|
||||||
@@ -47,9 +48,15 @@ export function render({ navigate, route }) {
|
|||||||
rows.forEach((row) => {
|
rows.forEach((row) => {
|
||||||
const el = document.createElement('button'); el.type = 'button'; el.className = 'card row profile-list-row';
|
const el = document.createElement('button'); el.type = 'button'; el.className = 'card row profile-list-row';
|
||||||
el.append(renderUserAvatar({ login: row.login, firstName: row.firstName, lastName: row.lastName, avatar: parseAvatar(row.avatarAr), size: 'md' }));
|
el.append(renderUserAvatar({ login: row.login, firstName: row.firstName, lastName: row.lastName, avatar: parseAvatar(row.avatarAr), size: 'md' }));
|
||||||
const fullName = [row.firstName, row.lastName].filter(Boolean).join(' ') || row.login;
|
const fullName = userDisplayName(row);
|
||||||
const t = document.createElement('div'); t.className = 'profile-list-row-text';
|
const t = document.createElement('div'); t.className = 'profile-list-row-text';
|
||||||
const marks = [row.relationType && row.relationType !== 'none' ? row.relationType : '', row.primaryConfirmed ? 'основной ✓' : '', row.shineConfirmed ? 'сияющий ✓' : ''].filter(Boolean).join(' · ');
|
const marks = [
|
||||||
|
row.relationType && row.relationType !== 'none' ? ({contact:'контакт',friend:'друг',close_friend:'близкий друг'}[row.relationType] || row.relationType) : '',
|
||||||
|
row.accountRole === 'primary' ? 'основной' : row.accountRole === 'non_voting' ? 'голос не учитывается' : '',
|
||||||
|
row.shineStatus === 'shining' ? 'сияющий' : row.shineStatus === 'not_interested' ? 'сияние неинтересно' : '',
|
||||||
|
row.primaryConfirmed ? 'основной подтверждён ✓' : '',
|
||||||
|
row.shineConfirmed ? 'сияющий подтверждён ✓' : '',
|
||||||
|
].filter(Boolean).join(' · ');
|
||||||
t.innerHTML = `<b>${fullName}</b><small>${String(row.login || '')}${marks ? ` · ${marks}` : ''}</small>`;
|
t.innerHTML = `<b>${fullName}</b><small>${String(row.login || '')}${marks ? ` · ${marks}` : ''}</small>`;
|
||||||
el.append(t); el.addEventListener('click', () => navigate(`SHiNE/${encodeURIComponent(row.login)}`)); body.append(el);
|
el.append(t); el.addEventListener('click', () => navigate(`SHiNE/${encodeURIComponent(row.login)}`)); body.append(el);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export function userDisplayName({ login = '', firstName = '', lastName = '' } = {}) {
|
||||||
|
const first = String(firstName || '').trim();
|
||||||
|
const last = String(lastName || '').trim();
|
||||||
|
const full = [first, last].filter(Boolean).join(' ').trim();
|
||||||
|
return full || String(login || '').trim() || 'unknown';
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ export const profileFieldDefs = [
|
|||||||
{ key: 'first_name', readKeys: ['first_name'], label: 'Имя', placeholder: 'Введите имя' },
|
{ key: 'first_name', readKeys: ['first_name'], label: 'Имя', placeholder: 'Введите имя' },
|
||||||
{ key: 'last_name', readKeys: ['last_name'], label: 'Фамилия', placeholder: 'Введите фамилию' },
|
{ key: 'last_name', readKeys: ['last_name'], label: 'Фамилия', placeholder: 'Введите фамилию' },
|
||||||
{ key: 'address', readKeys: ['address'], label: 'Адрес', placeholder: 'Город, улица, дом' },
|
{ key: 'address', readKeys: ['address'], label: 'Адрес', placeholder: 'Город, улица, дом' },
|
||||||
{ key: 'web', readKeys: ['web'], label: 'Веб', placeholder: 'Сайт или профиль' },
|
{ key: 'web', readKeys: ['web'], label: 'Links', placeholder: 'Сайт, профиль или другая ссылка' },
|
||||||
{ key: 'phone', readKeys: ['phone'], label: 'Телефон', placeholder: '+7 ...' },
|
{ key: 'phone', readKeys: ['phone'], label: 'Телефон', placeholder: '+7 ...' },
|
||||||
{ key: 'about', readKeys: ['about'], label: 'О себе', placeholder: 'Коротко расскажите о себе', maxLength: 160, multiline: true },
|
{ key: 'about', readKeys: ['about'], label: 'О себе', placeholder: 'Коротко расскажите о себе', maxLength: 160, multiline: true },
|
||||||
{ key: 'spiritual_path', readKeys: ['spiritual_path'], label: 'Духовный путь', placeholder: 'Расскажите о своём духовном пути, опыте, практиках и взглядах', maxLength: 5000, multiline: true },
|
{ key: 'spiritual_path', readKeys: ['spiritual_path'], label: 'Духовный путь', placeholder: 'Расскажите о своём духовном пути, опыте, практиках и взглядах', maxLength: 5000, multiline: true },
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
.badge-line { display: flex; gap: 8px; flex-wrap: wrap; }
|
.badge-line { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||||
.badge { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 999px; padding: 4px 10px; font-size: 11px; color: var(--text-muted); background: #0d0d0d; }
|
.badge { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 999px; padding: 4px 10px; font-size: 11px; color: var(--text-muted); background: #0d0d0d; }
|
||||||
.badge.ok { color: #7dcc7d; border-color: #2a4a2a; background: #1a2e1a; }
|
.badge.ok { color: #7dcc7d; border-color: #2a4a2a; background: #1a2e1a; }
|
||||||
.badge.warn { color: #ffd37a; border-color: #5f4b22; background: #2f2614; }
|
.badge.warn { color: #ffd37a; border-color: transparent; background: #2f2614; }
|
||||||
.badge.err { color: #f08080; border-color: #5a2a2a; background: #2e1a1a; }
|
.badge.err { color: #f08080; border-color: #5a2a2a; background: #2e1a1a; }
|
||||||
.details-wrap { margin-top: 12px; }
|
.details-wrap { margin-top: 12px; }
|
||||||
.details-wrap details { border: 1px solid var(--border); border-radius: var(--radius); background: #121212; }
|
.details-wrap details { border: 1px solid var(--border); border-radius: var(--radius); background: #121212; }
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
.expected-row:last-child { margin-bottom:0; }
|
.expected-row:last-child { margin-bottom:0; }
|
||||||
.expected-lbl { font-size:11px; color:var(--text-muted); margin-bottom:4px; }
|
.expected-lbl { font-size:11px; color:var(--text-muted); margin-bottom:4px; }
|
||||||
.expected-val { font-family:monospace; font-size:11px; word-break:break-all; }
|
.expected-val { font-family:monospace; font-size:11px; word-break:break-all; }
|
||||||
.gen-msg.warn { display:block; background:#2f2614; border:1px solid #5f4b22; color:#ffd37a; }
|
.gen-msg.warn { display:block; background:#2f2614; border: 1px solid transparent; color:#ffd37a; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+222
-142
@@ -21,7 +21,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
background: rgba(14, 21, 35, 0.92);
|
background: rgba(14, 21, 35, 0.92);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.18);
|
border: 1px solid transparent;
|
||||||
box-shadow: 0 10px 26px rgba(4, 8, 16, 0.28);
|
box-shadow: 0 10px 26px rgba(4, 8, 16, 0.28);
|
||||||
backdrop-filter: blur(14px);
|
backdrop-filter: blur(14px);
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,8 @@
|
|||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-icon-svg--phone {
|
.header-icon-svg--phone,
|
||||||
|
.header-icon-svg--search {
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
@@ -171,7 +172,7 @@
|
|||||||
.secondary-btn:hover,
|
.secondary-btn:hover,
|
||||||
.destructive-btn:hover,
|
.destructive-btn:hover,
|
||||||
.ghost-btn:hover {
|
.ghost-btn:hover {
|
||||||
border-color: var(--accent);
|
border-color: transparent;
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +190,7 @@
|
|||||||
|
|
||||||
.primary-btn {
|
.primary-btn {
|
||||||
background: linear-gradient(120deg, rgba(218, 179, 87, 0.95), rgba(184, 137, 54, 0.92));
|
background: linear-gradient(120deg, rgba(218, 179, 87, 0.95), rgba(184, 137, 54, 0.92));
|
||||||
border-color: rgba(242, 210, 129, 0.72);
|
border-color: transparent;
|
||||||
color: #101426;
|
color: #101426;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +507,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.profile-relative-suggest-item:hover {
|
.profile-relative-suggest-item:hover {
|
||||||
border-color: rgba(216, 178, 95, 0.52);
|
border-color: transparent;
|
||||||
color: #f3dca8;
|
color: #f3dca8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -810,7 +811,7 @@
|
|||||||
|
|
||||||
.shine-btn:focus-visible,
|
.shine-btn:focus-visible,
|
||||||
.shine-local-demo-btn:focus-visible {
|
.shine-local-demo-btn:focus-visible {
|
||||||
outline: 2px solid rgba(235, 197, 117, 0.72);
|
outline: 2px solid transparent;
|
||||||
outline-offset: 3px;
|
outline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,8 +1062,7 @@
|
|||||||
|
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .input:focus,
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .input:focus,
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .select:focus {
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .select:focus {
|
||||||
border-color: rgba(222, 184, 99, 0.76);
|
border-color: transparent;
|
||||||
box-shadow: 0 0 0 3px rgba(222, 184, 99, 0.13);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .auth-footer-actions {
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .auth-footer-actions {
|
||||||
@@ -1085,7 +1085,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .primary-btn {
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .primary-btn {
|
||||||
border-color: rgba(255, 226, 163, 0.5);
|
border-color: transparent;
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(255, 239, 198, 0.16), transparent 31%),
|
linear-gradient(180deg, rgba(255, 239, 198, 0.16), transparent 31%),
|
||||||
linear-gradient(180deg, rgba(144, 105, 55, 0.66), rgba(47, 34, 27, 0.76));
|
linear-gradient(180deg, rgba(144, 105, 55, 0.66), rgba(47, 34, 27, 0.76));
|
||||||
@@ -1104,7 +1104,7 @@
|
|||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .secondary-btn:hover,
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .secondary-btn:hover,
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .ghost-btn:hover,
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .ghost-btn:hover,
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .link-card:hover {
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .link-card:hover {
|
||||||
border-color: rgba(231, 202, 137, 0.66);
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .preauth-local-demo-btn {
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .preauth-local-demo-btn {
|
||||||
@@ -1121,7 +1121,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .registration-progress {
|
.screen-content.preauth-flow:not(:has(> .auth-screen--welcome)) .registration-progress {
|
||||||
border-color: rgba(226, 193, 124, 0.38);
|
border-color: transparent;
|
||||||
background: rgba(3, 9, 21, 0.62);
|
background: rgba(3, 9, 21, 0.62);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1708,7 +1708,6 @@
|
|||||||
|
|
||||||
.toolbar-connection-indicator.is-connecting .toolbar-connection-dot {
|
.toolbar-connection-indicator.is-connecting .toolbar-connection-dot {
|
||||||
background: #f0c56b;
|
background: #f0c56b;
|
||||||
box-shadow: 0 0 0 2px rgba(240, 197, 107, 0.22);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-connection-indicator.is-disconnected .toolbar-connection-dot {
|
.toolbar-connection-indicator.is-disconnected .toolbar-connection-dot {
|
||||||
@@ -2456,7 +2455,6 @@
|
|||||||
|
|
||||||
.pwa-diag-indicator.is-warn {
|
.pwa-diag-indicator.is-warn {
|
||||||
background: #f0c56b;
|
background: #f0c56b;
|
||||||
box-shadow: 0 0 0 3px rgba(240, 197, 107, 0.18);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pwa-diag-indicator.is-bad {
|
.pwa-diag-indicator.is-bad {
|
||||||
@@ -3033,7 +3031,7 @@ textarea.input {
|
|||||||
|
|
||||||
.ar-attachment-history-tile {
|
.ar-attachment-history-tile {
|
||||||
background: linear-gradient(135deg, rgba(15, 23, 42, 0.96), rgba(22, 36, 53, 0.96));
|
background: linear-gradient(135deg, rgba(15, 23, 42, 0.96), rgba(22, 36, 53, 0.96));
|
||||||
border: 1px solid rgba(212, 175, 55, 0.2);
|
border: 1px solid transparent;
|
||||||
border-radius: 0.52rem;
|
border-radius: 0.52rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: #f8fafc;
|
color: #f8fafc;
|
||||||
@@ -3185,7 +3183,7 @@ textarea.input {
|
|||||||
.arweave-uploads-toolbar {
|
.arweave-uploads-toolbar {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: linear-gradient(135deg, rgba(15, 23, 42, 0.98), rgba(30, 41, 59, 0.98));
|
background: linear-gradient(135deg, rgba(15, 23, 42, 0.98), rgba(30, 41, 59, 0.98));
|
||||||
border-bottom: 1px solid rgba(212, 175, 55, 0.22);
|
border-bottom: 1px solid transparent;
|
||||||
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
|
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -3228,7 +3226,7 @@ textarea.input {
|
|||||||
|
|
||||||
.arweave-uploads-menu {
|
.arweave-uploads-menu {
|
||||||
background: rgba(15, 23, 42, 0.98);
|
background: rgba(15, 23, 42, 0.98);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.24);
|
border: 1px solid transparent;
|
||||||
border-radius: 0.85rem;
|
border-radius: 0.85rem;
|
||||||
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
|
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -3607,7 +3605,7 @@ textarea.input {
|
|||||||
height: 18px;
|
height: 18px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid rgba(255, 214, 130, 0.95);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(180deg, rgba(255, 219, 145, 0.98), rgba(232, 165, 64, 0.98));
|
background: linear-gradient(180deg, rgba(255, 219, 145, 0.98), rgba(232, 165, 64, 0.98));
|
||||||
color: #3a2003;
|
color: #3a2003;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@@ -3884,7 +3882,7 @@ textarea.input {
|
|||||||
background:
|
background:
|
||||||
radial-gradient(circle at 18% -120%, rgba(228, 186, 94, 0.28), transparent 48%),
|
radial-gradient(circle at 18% -120%, rgba(228, 186, 94, 0.28), transparent 48%),
|
||||||
linear-gradient(160deg, rgba(14, 25, 47, 0.98), rgba(7, 16, 34, 0.98));
|
linear-gradient(160deg, rgba(14, 25, 47, 0.98), rgba(7, 16, 34, 0.98));
|
||||||
border: 1px solid rgba(197, 160, 85, 0.38);
|
border: 1px solid transparent;
|
||||||
box-shadow: 0 18px 32px rgba(2, 6, 13, 0.62);
|
box-shadow: 0 18px 32px rgba(2, 6, 13, 0.62);
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
padding: 9px;
|
padding: 9px;
|
||||||
@@ -3901,7 +3899,7 @@ textarea.input {
|
|||||||
background:
|
background:
|
||||||
linear-gradient(145deg, rgba(220, 181, 94, 0.32), rgba(39, 66, 122, 0.3)),
|
linear-gradient(145deg, rgba(220, 181, 94, 0.32), rgba(39, 66, 122, 0.3)),
|
||||||
rgba(20, 35, 64, 0.62);
|
rgba(20, 35, 64, 0.62);
|
||||||
border: 1px solid rgba(220, 183, 100, 0.44);
|
border: 1px solid transparent;
|
||||||
color: #f7e2ad;
|
color: #f7e2ad;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 242, 204, 0.42);
|
box-shadow: inset 0 1px 0 rgba(255, 242, 204, 0.42);
|
||||||
}
|
}
|
||||||
@@ -3912,7 +3910,7 @@ textarea.input {
|
|||||||
|
|
||||||
.modal-card {
|
.modal-card {
|
||||||
background: linear-gradient(165deg, rgba(16, 31, 58, 0.97), rgba(10, 18, 36, 0.97));
|
background: linear-gradient(165deg, rgba(16, 31, 58, 0.97), rgba(10, 18, 36, 0.97));
|
||||||
border-color: rgba(216, 179, 93, 0.4);
|
border-color: transparent;
|
||||||
box-shadow: 0 20px 38px rgba(2, 6, 12, 0.55);
|
box-shadow: 0 20px 38px rgba(2, 6, 12, 0.55);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3981,7 +3979,7 @@ textarea.input {
|
|||||||
background:
|
background:
|
||||||
linear-gradient(168deg, rgba(16, 31, 58, 0.95), rgba(8, 17, 34, 0.98)),
|
linear-gradient(168deg, rgba(16, 31, 58, 0.95), rgba(8, 17, 34, 0.98)),
|
||||||
radial-gradient(circle at 50% 0%, rgba(53, 90, 165, 0.2), transparent 52%);
|
radial-gradient(circle at 50% 0%, rgba(53, 90, 165, 0.2), transparent 52%);
|
||||||
border: 1px solid rgba(176, 144, 74, 0.3);
|
border: 1px solid transparent;
|
||||||
box-shadow: 0 14px 30px rgba(2, 7, 15, 0.54);
|
box-shadow: 0 14px 30px rgba(2, 7, 15, 0.54);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3999,7 +3997,7 @@ textarea.input {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-color: rgba(214, 177, 95, 0.42);
|
border-color: transparent;
|
||||||
box-shadow: 0 14px 30px rgba(4, 9, 20, 0.54);
|
box-shadow: 0 14px 30px rgba(4, 9, 20, 0.54);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4008,7 +4006,7 @@ textarea.input {
|
|||||||
width: 62px;
|
width: 62px;
|
||||||
height: 62px;
|
height: 62px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid rgba(220, 182, 98, 0.64);
|
border: 1px solid transparent;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at 45% 40%, rgba(247, 217, 145, 0.9), rgba(199, 146, 61, 0.95) 46%, rgba(127, 88, 36, 0.96) 100%);
|
radial-gradient(circle at 45% 40%, rgba(247, 217, 145, 0.9), rgba(199, 146, 61, 0.95) 46%, rgba(127, 88, 36, 0.96) 100%);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
@@ -4028,7 +4026,7 @@ textarea.input {
|
|||||||
.channels-hero-emblem::after {
|
.channels-hero-emblem::after {
|
||||||
inset: 7px;
|
inset: 7px;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-color: rgba(228, 192, 109, 0.66);
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channels-hero-copy {
|
.channels-hero-copy {
|
||||||
@@ -4072,7 +4070,6 @@ textarea.input {
|
|||||||
height: 9px;
|
height: 9px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #ddb86f;
|
background: #ddb86f;
|
||||||
box-shadow: 0 0 0 6px rgba(221, 184, 111, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.channels-help-card strong {
|
.channels-help-card strong {
|
||||||
@@ -4096,7 +4093,7 @@ textarea.input {
|
|||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 10px 13px;
|
padding: 10px 13px;
|
||||||
color: #ead3a0;
|
color: #ead3a0;
|
||||||
border: 1px solid rgba(214, 175, 89, 0.4);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(134deg, rgba(180, 140, 62, 0.22), rgba(23, 44, 84, 0.3));
|
background: linear-gradient(134deg, rgba(180, 140, 62, 0.22), rgba(23, 44, 84, 0.3));
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.35;
|
line-height: 1.35;
|
||||||
@@ -4152,7 +4149,7 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channels-list-empty {
|
.channels-list-empty {
|
||||||
border: 1px dashed rgba(209, 172, 87, 0.35);
|
border: 1px dashed transparent;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
color: #aeb9d8;
|
color: #aeb9d8;
|
||||||
@@ -4160,7 +4157,7 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channels-divider {
|
.channels-divider {
|
||||||
border-top-color: rgba(211, 170, 86, 0.22);
|
border-top-color: transparent;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4171,7 +4168,7 @@ textarea.input {
|
|||||||
padding: 14px 13px;
|
padding: 14px 13px;
|
||||||
width: min(100%, 340px);
|
width: min(100%, 340px);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid rgba(193, 157, 82, 0.28);
|
border: 1px solid transparent;
|
||||||
background:
|
background:
|
||||||
linear-gradient(150deg, rgba(16, 31, 58, 0.9), rgba(10, 20, 40, 0.94)),
|
linear-gradient(150deg, rgba(16, 31, 58, 0.9), rgba(10, 20, 40, 0.94)),
|
||||||
radial-gradient(circle at 100% 0%, rgba(72, 106, 179, 0.22), transparent 46%);
|
radial-gradient(circle at 100% 0%, rgba(72, 106, 179, 0.22), transparent 46%);
|
||||||
@@ -4181,7 +4178,7 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channel-row:hover {
|
.channel-row:hover {
|
||||||
border-color: rgba(224, 188, 106, 0.52);
|
border-color: transparent;
|
||||||
box-shadow: 0 10px 22px rgba(2, 8, 16, 0.44);
|
box-shadow: 0 10px 22px rgba(2, 8, 16, 0.44);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
@@ -4264,7 +4261,7 @@ textarea.input {
|
|||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid rgba(205, 166, 86, 0.42);
|
border: 1px solid transparent;
|
||||||
color: #f1d49a;
|
color: #f1d49a;
|
||||||
background: rgba(191, 149, 66, 0.16);
|
background: rgba(191, 149, 66, 0.16);
|
||||||
}
|
}
|
||||||
@@ -4352,7 +4349,7 @@ textarea.input {
|
|||||||
margin: 10px 0 12px;
|
margin: 10px 0 12px;
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid rgba(244, 202, 102, 0.46);
|
border: 1px solid transparent;
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(50, 39, 14, 0.9), rgba(22, 25, 39, 0.9));
|
linear-gradient(180deg, rgba(50, 39, 14, 0.9), rgba(22, 25, 39, 0.9));
|
||||||
color: #ffe6a7;
|
color: #ffe6a7;
|
||||||
@@ -4363,7 +4360,7 @@ textarea.input {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-shadow: 0 0 0 1px rgba(255, 226, 155, 0.08), 0 10px 24px rgba(6, 10, 20, 0.35);
|
box-shadow: 0 10px 24px rgba(6, 10, 20, 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-unread-line::before,
|
.channel-unread-line::before,
|
||||||
@@ -4484,7 +4481,7 @@ textarea.input {
|
|||||||
.channel-message-kind-badge--rating {
|
.channel-message-kind-badge--rating {
|
||||||
color: #ffe8b0;
|
color: #ffe8b0;
|
||||||
background: rgba(124, 92, 28, 0.36);
|
background: rgba(124, 92, 28, 0.36);
|
||||||
border: 1px solid rgba(255, 214, 117, 0.28);
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-message-kind-badge--status {
|
.channel-message-kind-badge--status {
|
||||||
@@ -4521,7 +4518,7 @@ textarea.input {
|
|||||||
|
|
||||||
.channels-screen .channel-message-card.is-rating,
|
.channels-screen .channel-message-card.is-rating,
|
||||||
.thread-node-card.is-rating {
|
.thread-node-card.is-rating {
|
||||||
border-color: rgba(255, 214, 117, 0.34);
|
border-color: transparent;
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(112, 84, 22, 0.12), rgba(20, 25, 35, 0.58)),
|
linear-gradient(180deg, rgba(112, 84, 22, 0.12), rgba(20, 25, 35, 0.58)),
|
||||||
rgba(20, 25, 35, 0.55);
|
rgba(20, 25, 35, 0.55);
|
||||||
@@ -4529,8 +4526,8 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channels-screen .channel-message-card.is-focus-flash {
|
.channels-screen .channel-message-card.is-focus-flash {
|
||||||
border-color: rgba(255, 214, 117, 0.5);
|
border-color: transparent;
|
||||||
box-shadow: 0 0 0 1px rgba(255, 214, 117, 0.28), 0 0 34px rgba(255, 214, 117, 0.16);
|
box-shadow: 0 0 34px rgba(255, 214, 117, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-message-body {
|
.channel-message-body {
|
||||||
@@ -4568,7 +4565,7 @@ textarea.input {
|
|||||||
max-width: min(88%, 460px);
|
max-width: min(88%, 460px);
|
||||||
margin: 0 auto 4px;
|
margin: 0 auto 4px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-color: rgba(255, 210, 130, 0.22);
|
border-color: transparent;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(89, 67, 31, 0.26);
|
background: rgba(89, 67, 31, 0.26);
|
||||||
color: rgba(255, 235, 188, 0.92);
|
color: rgba(255, 235, 188, 0.92);
|
||||||
@@ -4626,7 +4623,7 @@ textarea.input {
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: linear-gradient(145deg, rgba(255, 214, 122, 0.22), rgba(56, 41, 22, 0.65));
|
background: linear-gradient(145deg, rgba(255, 214, 122, 0.22), rgba(56, 41, 22, 0.65));
|
||||||
border: 1px solid rgba(255, 226, 155, 0.24);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 236, 194, 0.95);
|
color: rgba(255, 236, 194, 0.95);
|
||||||
font-size: calc(var(--channel-avatar-size) * 0.42);
|
font-size: calc(var(--channel-avatar-size) * 0.42);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -4765,7 +4762,7 @@ textarea.input {
|
|||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
max-width: 180px;
|
max-width: 180px;
|
||||||
padding: 5px 8px;
|
padding: 5px 8px;
|
||||||
border: 1px solid rgba(255, 220, 140, 0.24);
|
border: 1px solid transparent;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: rgba(20, 20, 24, 0.94);
|
background: rgba(20, 20, 24, 0.94);
|
||||||
color: rgba(255, 244, 210, 0.96);
|
color: rgba(255, 244, 210, 0.96);
|
||||||
@@ -4854,14 +4851,14 @@ textarea.input {
|
|||||||
|
|
||||||
.thread-summary {
|
.thread-summary {
|
||||||
color: #efd9a4;
|
color: #efd9a4;
|
||||||
border-color: rgba(212, 171, 90, 0.36);
|
border-color: transparent;
|
||||||
background: linear-gradient(130deg, rgba(178, 137, 58, 0.2), rgba(24, 41, 74, 0.24));
|
background: linear-gradient(130deg, rgba(178, 137, 58, 0.2), rgba(24, 41, 74, 0.24));
|
||||||
}
|
}
|
||||||
|
|
||||||
.thread-node-card {
|
.thread-node-card {
|
||||||
gap: 9px;
|
gap: 9px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border-color: rgba(183, 150, 79, 0.3);
|
border-color: transparent;
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4973,7 +4970,7 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.thread-block--focus {
|
.thread-block--focus {
|
||||||
border-color: rgba(214, 177, 95, 0.34);
|
border-color: transparent;
|
||||||
background: linear-gradient(160deg, rgba(33, 44, 72, 0.68), rgba(12, 20, 36, 0.8));
|
background: linear-gradient(160deg, rgba(33, 44, 72, 0.68), rgba(12, 20, 36, 0.8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5113,7 +5110,7 @@ textarea.input {
|
|||||||
max-width: min(92vw, 420px);
|
max-width: min(92vw, 420px);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 11px 14px;
|
padding: 11px 14px;
|
||||||
border: 1px solid rgba(223, 188, 110, 0.45);
|
border: 1px solid transparent;
|
||||||
color: #f2dca8;
|
color: #f2dca8;
|
||||||
background: rgba(10, 14, 23, 0.8);
|
background: rgba(10, 14, 23, 0.8);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
@@ -5146,7 +5143,7 @@ textarea.input {
|
|||||||
.skeleton-line {
|
.skeleton-line {
|
||||||
height: 10px;
|
height: 10px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: linear-gradient(100deg, rgba(180, 151, 80, 0.16), rgba(228, 192, 109, 0.45), rgba(180, 151, 80, 0.16));
|
background: linear-gradient(100deg, rgba(93, 117, 154, 0.10), rgba(167, 191, 226, 0.24), rgba(93, 117, 154, 0.10));
|
||||||
background-size: 220% 100%;
|
background-size: 220% 100%;
|
||||||
animation: channels-shimmer 1.2s linear infinite;
|
animation: channels-shimmer 1.2s linear infinite;
|
||||||
}
|
}
|
||||||
@@ -5166,7 +5163,7 @@ textarea.input {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
border: 1px solid rgba(188, 152, 79, 0.34);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(165deg, rgba(12, 24, 46, 0.92), rgba(9, 17, 34, 0.96));
|
background: linear-gradient(165deg, rgba(12, 24, 46, 0.92), rgba(9, 17, 34, 0.96));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5278,13 +5275,13 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channels-tab-btn.is-active {
|
.channels-tab-btn.is-active {
|
||||||
border-color: rgba(218, 183, 100, 0.48);
|
border-color: transparent;
|
||||||
color: #f4d99e;
|
color: #f4d99e;
|
||||||
background: linear-gradient(160deg, rgba(193, 154, 76, 0.22), rgba(18, 33, 62, 0.64));
|
background: linear-gradient(160deg, rgba(193, 154, 76, 0.22), rgba(18, 33, 62, 0.64));
|
||||||
}
|
}
|
||||||
|
|
||||||
.channels-empty-state {
|
.channels-empty-state {
|
||||||
border: 1px dashed rgba(199, 164, 90, 0.36);
|
border: 1px dashed transparent;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -5338,7 +5335,7 @@ textarea.input {
|
|||||||
width: 34px;
|
width: 34px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid rgba(185, 154, 83, 0.42);
|
border: 1px solid transparent;
|
||||||
background: rgba(14, 25, 48, 0.82);
|
background: rgba(14, 25, 48, 0.82);
|
||||||
color: #efd9a4;
|
color: #efd9a4;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -5353,7 +5350,7 @@ textarea.input {
|
|||||||
z-index: 25;
|
z-index: 25;
|
||||||
width: min(240px, 70vw);
|
width: min(240px, 70vw);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
border: 1px solid rgba(206, 170, 90, 0.38);
|
border: 1px solid transparent;
|
||||||
background: rgba(10, 14, 23, 0.8);
|
background: rgba(10, 14, 23, 0.8);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
box-shadow: 0 16px 32px rgba(1, 5, 12, 0.62);
|
box-shadow: 0 16px 32px rgba(1, 5, 12, 0.62);
|
||||||
@@ -5418,7 +5415,7 @@ textarea.input {
|
|||||||
|
|
||||||
.channel-toggle-btn.is-on {
|
.channel-toggle-btn.is-on {
|
||||||
background: rgba(211, 173, 92, 0.34);
|
background: rgba(211, 173, 92, 0.34);
|
||||||
border-color: rgba(219, 182, 101, 0.6);
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-toggle-btn.is-on::after {
|
.channel-toggle-btn.is-on::after {
|
||||||
@@ -5477,7 +5474,7 @@ textarea.input {
|
|||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
padding: 4px 9px;
|
padding: 4px 9px;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
border: 1px solid rgba(224, 190, 117, 0.38);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(180deg, rgba(57, 74, 119, 0.34), rgba(18, 29, 54, 0.34));
|
background: linear-gradient(180deg, rgba(57, 74, 119, 0.34), rgba(18, 29, 54, 0.34));
|
||||||
color: #f1d99c;
|
color: #f1d99c;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@@ -5596,7 +5593,7 @@ textarea.input {
|
|||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.45);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(160deg, rgba(16, 24, 38, 0.92), rgba(12, 18, 30, 0.86));
|
background: linear-gradient(160deg, rgba(16, 24, 38, 0.92), rgba(12, 18, 30, 0.86));
|
||||||
color: rgba(255, 227, 150, 0.95);
|
color: rgba(255, 227, 150, 0.95);
|
||||||
box-shadow: 0 12px 26px rgba(3, 8, 18, 0.45), 0 0 20px rgba(212, 175, 55, 0.16);
|
box-shadow: 0 12px 26px rgba(3, 8, 18, 0.45), 0 0 20px rgba(212, 175, 55, 0.16);
|
||||||
@@ -5692,7 +5689,7 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channel-search-item:hover {
|
.channel-search-item:hover {
|
||||||
border-color: rgba(216, 178, 95, 0.52);
|
border-color: transparent;
|
||||||
color: #f3dca8;
|
color: #f3dca8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5793,7 +5790,7 @@ textarea.input {
|
|||||||
|
|
||||||
.channels-tab-btn.is-active {
|
.channels-tab-btn.is-active {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-bottom-color: rgba(255, 200, 50, 0.9);
|
border-bottom-color: transparent;
|
||||||
color: rgba(255, 200, 50, 0.9);
|
color: rgba(255, 200, 50, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5855,7 +5852,7 @@ textarea.input {
|
|||||||
.channels-bottom-action,
|
.channels-bottom-action,
|
||||||
.primary-btn.channel-main-action {
|
.primary-btn.channel-main-action {
|
||||||
background: rgba(255, 180, 0, 0.12);
|
background: rgba(255, 180, 0, 0.12);
|
||||||
border: 1px solid rgba(255, 180, 0, 0.35);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 200, 50, 0.9);
|
color: rgba(255, 200, 50, 0.9);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
@@ -5958,7 +5955,7 @@ textarea.input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channels-screen--channel .page-header .channel-header-entrypoint-btn {
|
.channels-screen--channel .page-header .channel-header-entrypoint-btn {
|
||||||
border: 1px solid rgba(224, 190, 117, 0.38);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(180deg, rgba(57, 74, 119, 0.34), rgba(18, 29, 54, 0.34));
|
background: linear-gradient(180deg, rgba(57, 74, 119, 0.34), rgba(18, 29, 54, 0.34));
|
||||||
color: #f1d99c;
|
color: #f1d99c;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -6060,7 +6057,7 @@ textarea.input {
|
|||||||
background: rgba(15, 18, 30, 0.92);
|
background: rgba(15, 18, 30, 0.92);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid rgba(255, 180, 0, 0.2);
|
border: 1px solid transparent;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
@@ -6123,7 +6120,7 @@ textarea.input {
|
|||||||
#channels-subscribe-modal .primary-btn,
|
#channels-subscribe-modal .primary-btn,
|
||||||
#thread-reply-modal .primary-btn {
|
#thread-reply-modal .primary-btn {
|
||||||
background: rgba(255, 180, 0, 0.15);
|
background: rgba(255, 180, 0, 0.15);
|
||||||
border: 1px solid rgba(255, 180, 0, 0.4);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 200, 50, 0.95);
|
color: rgba(255, 200, 50, 0.95);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 13px 24px;
|
padding: 13px 24px;
|
||||||
@@ -6170,7 +6167,7 @@ textarea.input {
|
|||||||
z-index: 12;
|
z-index: 12;
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
-webkit-backdrop-filter: blur(16px);
|
-webkit-backdrop-filter: blur(16px);
|
||||||
border-bottom: 1px solid rgba(212, 175, 55, 0.24);
|
border-bottom: 1px solid transparent;
|
||||||
background: rgba(10, 12, 18, 0.72);
|
background: rgba(10, 12, 18, 0.72);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6195,7 +6192,7 @@ textarea.input {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.dm-dialog-card:focus-visible { outline: 2px solid var(--rel-link); outline-offset: 2px; }
|
.dm-dialog-card:focus-visible { outline: 2px solid var(--rel-link); outline-offset: 2px; }
|
||||||
.dm-card--family { border-color: rgba(240, 184, 46, 0.42); } /* линия связи: gold (семья) */
|
.dm-card--family { border-color: transparent; } /* линия связи: gold (семья) */
|
||||||
.dm-card--shining { border-color: rgba(104, 216, 255, 0.45); } /* линия связи: cyan (сияющий) */
|
.dm-card--shining { border-color: rgba(104, 216, 255, 0.45); } /* линия связи: cyan (сияющий) */
|
||||||
|
|
||||||
.dm-screen .list-item .avatar {
|
.dm-screen .list-item .avatar {
|
||||||
@@ -6204,7 +6201,7 @@ textarea.input {
|
|||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.45);
|
border: 1px solid transparent;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at 26% 24%, rgba(196, 165, 255, 0.95), rgba(78, 87, 197, 0.9) 58%, rgba(36, 45, 121, 0.9));
|
radial-gradient(circle at 26% 24%, rgba(196, 165, 255, 0.95), rgba(78, 87, 197, 0.9) 58%, rgba(36, 45, 121, 0.9));
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
@@ -6263,7 +6260,7 @@ textarea.input {
|
|||||||
.dm-head-plus {
|
.dm-head-plus {
|
||||||
justify-self: end; width: 48px; height: 48px; border-radius: 50%;
|
justify-self: end; width: 48px; height: 48px; border-radius: 50%;
|
||||||
display: grid; place-items: center; font-size: 24px; line-height: 1; font-weight: 300;
|
display: grid; place-items: center; font-size: 24px; line-height: 1; font-weight: 300;
|
||||||
color: #FFD98A; border: 1.5px solid rgba(240, 184, 46, 0.6);
|
color: #FFD98A; border: 1.5px solid transparent;
|
||||||
background: rgba(12, 12, 16, 0.66);
|
background: rgba(12, 12, 16, 0.66);
|
||||||
box-shadow: 0 0 20px rgba(240, 184, 46, 0.32), 0 0 6px rgba(240, 184, 46, 0.28), inset 0 0 12px rgba(240, 184, 46, 0.12);
|
box-shadow: 0 0 20px rgba(240, 184, 46, 0.32), 0 0 6px rgba(240, 184, 46, 0.28), inset 0 0 12px rgba(240, 184, 46, 0.12);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -6500,7 +6497,7 @@ html, body { overflow-x: hidden; }
|
|||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
padding: 7px 14px;
|
padding: 7px 14px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.24);
|
border: 1px solid transparent;
|
||||||
background: rgba(9, 15, 26, 0.92);
|
background: rgba(9, 15, 26, 0.92);
|
||||||
color: rgba(245, 226, 179, 0.96);
|
color: rgba(245, 226, 179, 0.96);
|
||||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
|
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
|
||||||
@@ -6518,8 +6515,8 @@ html, body { overflow-x: hidden; }
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 2px solid rgba(255, 234, 186, 0.22);
|
border: 2px solid transparent;
|
||||||
border-top-color: rgba(242, 201, 92, 0.96);
|
border-top-color: transparent;
|
||||||
box-shadow: 0 0 10px rgba(212, 175, 55, 0.18);
|
box-shadow: 0 0 10px rgba(212, 175, 55, 0.18);
|
||||||
animation: dm-history-loader-spin 0.85s linear infinite;
|
animation: dm-history-loader-spin 0.85s linear infinite;
|
||||||
}
|
}
|
||||||
@@ -6567,7 +6564,7 @@ html, body { overflow-x: hidden; }
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
border-top: 1px solid rgba(212, 175, 55, 0.22);
|
border-top: 1px solid transparent;
|
||||||
background: rgba(8, 12, 20, 0.9);
|
background: rgba(8, 12, 20, 0.9);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(12px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
@@ -6581,7 +6578,7 @@ html, body { overflow-x: hidden; }
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.35);
|
border: 1px solid transparent;
|
||||||
background: rgba(212, 175, 55, 0.12);
|
background: rgba(212, 175, 55, 0.12);
|
||||||
color: rgba(255, 233, 176, 0.96);
|
color: rgba(255, 233, 176, 0.96);
|
||||||
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.22);
|
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.22);
|
||||||
@@ -6833,7 +6830,7 @@ html, body { overflow-x: hidden; }
|
|||||||
.dm-screen .input,
|
.dm-screen .input,
|
||||||
.dm-input {
|
.dm-input {
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(255, 255, 255, 0.04);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.35);
|
border: 1px solid transparent;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
color: rgba(255, 255, 255, 0.92);
|
color: rgba(255, 255, 255, 0.92);
|
||||||
}
|
}
|
||||||
@@ -6844,7 +6841,7 @@ html, body { overflow-x: hidden; }
|
|||||||
|
|
||||||
.dm-send-btn {
|
.dm-send-btn {
|
||||||
background: rgba(212, 175, 55, 0.2);
|
background: rgba(212, 175, 55, 0.2);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.45);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 217, 128, 0.98);
|
color: rgba(255, 217, 128, 0.98);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -6865,7 +6862,7 @@ html, body { overflow-x: hidden; }
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.22);
|
border: 1px solid transparent;
|
||||||
background: rgba(10, 12, 18, 0.96);
|
background: rgba(10, 12, 18, 0.96);
|
||||||
backdrop-filter: blur(22px);
|
backdrop-filter: blur(22px);
|
||||||
-webkit-backdrop-filter: blur(22px);
|
-webkit-backdrop-filter: blur(22px);
|
||||||
@@ -6933,7 +6930,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: rgba(18, 24, 38, 0.42);
|
background: rgba(18, 24, 38, 0.42);
|
||||||
backdrop-filter: blur(25px);
|
backdrop-filter: blur(25px);
|
||||||
-webkit-backdrop-filter: blur(25px);
|
-webkit-backdrop-filter: blur(25px);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.32);
|
border: 1px solid transparent;
|
||||||
color: rgba(225, 233, 248, 0.86);
|
color: rgba(225, 233, 248, 0.86);
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
@@ -6943,7 +6940,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background:
|
background:
|
||||||
radial-gradient(circle at 18% -120%, rgba(228, 186, 94, 0.28), transparent 48%),
|
radial-gradient(circle at 18% -120%, rgba(228, 186, 94, 0.28), transparent 48%),
|
||||||
linear-gradient(160deg, rgba(14, 25, 47, 0.98), rgba(7, 16, 34, 0.98));
|
linear-gradient(160deg, rgba(14, 25, 47, 0.98), rgba(7, 16, 34, 0.98));
|
||||||
border: 1px solid rgba(197, 160, 85, 0.38);
|
border: 1px solid transparent;
|
||||||
box-shadow: 0 18px 32px rgba(2, 6, 13, 0.48);
|
box-shadow: 0 18px 32px rgba(2, 6, 13, 0.48);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6958,7 +6955,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background:
|
background:
|
||||||
linear-gradient(145deg, rgba(220, 181, 94, 0.32), rgba(39, 66, 122, 0.3)),
|
linear-gradient(145deg, rgba(220, 181, 94, 0.32), rgba(39, 66, 122, 0.3)),
|
||||||
rgba(20, 35, 64, 0.62);
|
rgba(20, 35, 64, 0.62);
|
||||||
border: 1px solid rgba(220, 183, 100, 0.44);
|
border: 1px solid transparent;
|
||||||
color: #f7e2ad;
|
color: #f7e2ad;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 242, 204, 0.42);
|
box-shadow: inset 0 1px 0 rgba(255, 242, 204, 0.42);
|
||||||
}
|
}
|
||||||
@@ -6976,7 +6973,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: rgba(18, 24, 38, 0.4);
|
background: rgba(18, 24, 38, 0.4);
|
||||||
backdrop-filter: blur(25px);
|
backdrop-filter: blur(25px);
|
||||||
-webkit-backdrop-filter: blur(25px);
|
-webkit-backdrop-filter: blur(25px);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.3);
|
border: 1px solid transparent;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7017,7 +7014,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background:
|
background:
|
||||||
linear-gradient(145deg, rgba(212, 175, 55, 0.22), rgba(68, 92, 171, 0.2)),
|
linear-gradient(145deg, rgba(212, 175, 55, 0.22), rgba(68, 92, 171, 0.2)),
|
||||||
rgba(18, 24, 38, 0.44);
|
rgba(18, 24, 38, 0.44);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.42);
|
border: 1px solid transparent;
|
||||||
color: #D4AF37;
|
color: #D4AF37;
|
||||||
text-shadow: 0 0 10px rgba(212, 175, 55, 0.6);
|
text-shadow: 0 0 10px rgba(212, 175, 55, 0.6);
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 238, 197, 0.3);
|
box-shadow: inset 0 1px 0 rgba(255, 238, 197, 0.3);
|
||||||
@@ -7033,7 +7030,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
-webkit-backdrop-filter: none;
|
-webkit-backdrop-filter: none;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.3);
|
border: 1px solid transparent;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7285,7 +7282,7 @@ html, body { overflow-x: hidden; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.channels-screen--add .page-header .icon-btn:hover {
|
.channels-screen--add .page-header .icon-btn:hover {
|
||||||
border-color: rgba(212, 175, 55, 0.44);
|
border-color: transparent;
|
||||||
color: rgba(255, 215, 126, 0.95);
|
color: rgba(255, 215, 126, 0.95);
|
||||||
background: rgba(255, 180, 0, 0.1);
|
background: rgba(255, 180, 0, 0.1);
|
||||||
transform: none;
|
transform: none;
|
||||||
@@ -7308,7 +7305,7 @@ html, body { overflow-x: hidden; }
|
|||||||
|
|
||||||
.channels-screen--add #submit-create-channel {
|
.channels-screen--add #submit-create-channel {
|
||||||
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.05));
|
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.05));
|
||||||
border: 1px solid rgba(212, 175, 55, 0.42);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 213, 118, 0.95);
|
color: rgba(255, 213, 118, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7558,7 +7555,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: rgba(20, 25, 35, 0.4);
|
background: rgba(20, 25, 35, 0.4);
|
||||||
backdrop-filter: blur(25px);
|
backdrop-filter: blur(25px);
|
||||||
-webkit-backdrop-filter: blur(25px);
|
-webkit-backdrop-filter: blur(25px);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.35);
|
border: 1px solid transparent;
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.37);
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.37);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7590,9 +7587,9 @@ html, body { overflow-x: hidden; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.profile-top-icon-btn.is-active {
|
.profile-top-icon-btn.is-active {
|
||||||
border-color: rgba(212, 175, 55, 0.46);
|
border-color: transparent;
|
||||||
background: rgba(212, 175, 55, 0.12);
|
background: rgba(212, 175, 55, 0.12);
|
||||||
box-shadow: 0 0 0 1px rgba(212, 175, 55, 0.08) inset, 0 10px 22px rgba(0, 0, 0, 0.22);
|
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.22);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-top-icon-btn:hover {
|
.profile-top-icon-btn:hover {
|
||||||
@@ -7796,7 +7793,7 @@ html, body { overflow-x: hidden; }
|
|||||||
|
|
||||||
.profile-screen .primary-btn {
|
.profile-screen .primary-btn {
|
||||||
background: rgba(212, 175, 55, 0.2);
|
background: rgba(212, 175, 55, 0.2);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.45);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 217, 128, 0.98);
|
color: rgba(255, 217, 128, 0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7833,7 +7830,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: rgba(20, 25, 35, 0.5);
|
background: rgba(20, 25, 35, 0.5);
|
||||||
backdrop-filter: blur(18px);
|
backdrop-filter: blur(18px);
|
||||||
-webkit-backdrop-filter: blur(18px);
|
-webkit-backdrop-filter: blur(18px);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.26);
|
border: 1px solid transparent;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7843,7 +7840,7 @@ html, body { overflow-x: hidden; }
|
|||||||
|
|
||||||
.notifications-screen .tab-btn.active {
|
.notifications-screen .tab-btn.active {
|
||||||
background: rgba(255, 180, 0, 0.12);
|
background: rgba(255, 180, 0, 0.12);
|
||||||
border: 1px solid rgba(255, 180, 0, 0.28);
|
border: 1px solid transparent;
|
||||||
color: rgba(255, 200, 50, 0.92);
|
color: rgba(255, 200, 50, 0.92);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7948,7 +7945,7 @@ html, body { overflow-x: hidden; }
|
|||||||
|
|
||||||
.channels-screen--list .channels-bottom-action {
|
.channels-screen--list .channels-bottom-action {
|
||||||
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.05));
|
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.05));
|
||||||
border: 1px solid rgba(212, 175, 55, 0.3);
|
border: 1px solid transparent;
|
||||||
color: #D4AF37;
|
color: #D4AF37;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
@@ -7961,7 +7958,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: rgba(18, 24, 38, 0.4) !important;
|
background: rgba(18, 24, 38, 0.4) !important;
|
||||||
backdrop-filter: blur(25px);
|
backdrop-filter: blur(25px);
|
||||||
-webkit-backdrop-filter: blur(25px);
|
-webkit-backdrop-filter: blur(25px);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.3) !important;
|
border: 1px solid transparent !important;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7977,7 +7974,7 @@ html, body { overflow-x: hidden; }
|
|||||||
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.05));
|
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.05));
|
||||||
backdrop-filter: blur(25px);
|
backdrop-filter: blur(25px);
|
||||||
-webkit-backdrop-filter: blur(25px);
|
-webkit-backdrop-filter: blur(25px);
|
||||||
border: 1px solid rgba(212, 175, 55, 0.3);
|
border: 1px solid transparent;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
color: #D4AF37;
|
color: #D4AF37;
|
||||||
}
|
}
|
||||||
@@ -8031,7 +8028,7 @@ html, body { overflow-x: hidden; }
|
|||||||
margin: 16px 20px 0;
|
margin: 16px 20px 0;
|
||||||
width: calc(100% - 40px);
|
width: calc(100% - 40px);
|
||||||
background: linear-gradient(135deg, #f5cf4f, #e2ad1f);
|
background: linear-gradient(135deg, #f5cf4f, #e2ad1f);
|
||||||
border: 1px solid rgba(255, 215, 97, 0.85);
|
border: 1px solid transparent;
|
||||||
color: #2f2200;
|
color: #2f2200;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
box-shadow: 0 14px 28px rgba(226, 173, 31, 0.24);
|
box-shadow: 0 14px 28px rgba(226, 173, 31, 0.24);
|
||||||
@@ -8097,7 +8094,7 @@ html, body { overflow-x: hidden; }
|
|||||||
.dm-head-menu-btn:focus-visible {
|
.dm-head-menu-btn:focus-visible {
|
||||||
outline: none;
|
outline: none;
|
||||||
background: rgba(240, 184, 46, 0.08);
|
background: rgba(240, 184, 46, 0.08);
|
||||||
box-shadow: inset 0 0 0 1px rgba(240, 184, 46, 0.24), 0 0 18px rgba(240, 184, 46, 0.12);
|
box-shadow: 0 0 18px rgba(240, 184, 46, 0.12);
|
||||||
}
|
}
|
||||||
.dm-head-menu-dots {
|
.dm-head-menu-dots {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
@@ -8123,7 +8120,7 @@ html, body { overflow-x: hidden; }
|
|||||||
width: max-content;
|
width: max-content;
|
||||||
min-width: 190px;
|
min-width: 190px;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border: 1px solid rgba(240, 184, 46, 0.26);
|
border: 1px solid transparent;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background: linear-gradient(155deg, rgba(22, 24, 31, 0.97), rgba(10, 12, 18, 0.97));
|
background: linear-gradient(155deg, rgba(22, 24, 31, 0.97), rgba(10, 12, 18, 0.97));
|
||||||
box-shadow: 0 16px 38px rgba(0, 0, 0, 0.42), 0 0 20px rgba(240, 184, 46, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
box-shadow: 0 16px 38px rgba(0, 0, 0, 0.42), 0 0 20px rgba(240, 184, 46, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||||
@@ -8141,8 +8138,8 @@ html, body { overflow-x: hidden; }
|
|||||||
width: 9px;
|
width: 9px;
|
||||||
height: 9px;
|
height: 9px;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
border-left: 1px solid rgba(240, 184, 46, 0.22);
|
border-left: 1px solid transparent;
|
||||||
border-top: 1px solid rgba(240, 184, 46, 0.22);
|
border-top: 1px solid transparent;
|
||||||
background: rgba(19, 21, 28, 0.98);
|
background: rgba(19, 21, 28, 0.98);
|
||||||
}
|
}
|
||||||
.dm-head-menu-item {
|
.dm-head-menu-item {
|
||||||
@@ -8312,7 +8309,7 @@ html, body { overflow-x: hidden; }
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
border: 1px solid rgba(212, 175, 55, 0.45);
|
border: 1px solid transparent;
|
||||||
background: linear-gradient(160deg, rgba(16, 24, 38, 0.94), rgba(12, 18, 30, 0.9));
|
background: linear-gradient(160deg, rgba(16, 24, 38, 0.94), rgba(12, 18, 30, 0.9));
|
||||||
color: rgba(255, 227, 150, 0.96);
|
color: rgba(255, 227, 150, 0.96);
|
||||||
box-shadow: 0 12px 26px rgba(3, 8, 18, 0.45), 0 0 20px rgba(212, 175, 55, 0.16);
|
box-shadow: 0 12px 26px rgba(3, 8, 18, 0.45), 0 0 20px rgba(212, 175, 55, 0.16);
|
||||||
@@ -8337,7 +8334,7 @@ html, body { overflow-x: hidden; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scroll-to-bottom-btn:focus-visible {
|
.scroll-to-bottom-btn:focus-visible {
|
||||||
outline: 2px solid rgba(255, 227, 150, 0.92);
|
outline: 2px solid transparent;
|
||||||
outline-offset: 3px;
|
outline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9623,22 +9620,15 @@ body.chat-topbar-overlay .composer-slot > * {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ===== Connections: restore canonical shared topbar (2026-08-22 14:00) =====
|
/* ===== «Связи»: симметричное затухание сверху и снизу =====
|
||||||
* The toolbar itself is the standard renderHeader() mounted in #topbar-slot.
|
* Граф занимает всю высоту экрана и продолжается под обеими панелями.
|
||||||
* Network-specific positioning must never move .screen-content under it.
|
* Размытия нет: верх и низ используют одинаковую зеркальную alpha-кривую.
|
||||||
* The graph fade lives inside .network-stage instead, so canvas layout cannot
|
* У внешнего края последняя треть панели уже полностью совпадает с фоном. */
|
||||||
* change toolbar height/position or overlap its controls. */
|
|
||||||
.topbar-slot:has(> .network-topbar)::before {
|
|
||||||
/* Keep the generic toolbar background/fade inside the toolbar bounds only. */
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Undo the previous Connections-only full-bleed-under-toolbar geometry. */
|
|
||||||
.screen-content.network-scroll-lock {
|
.screen-content.network-scroll-lock {
|
||||||
top: calc(var(--call-minimized-bar-height, 0px) + var(--topbar-height, 0px)) !important;
|
top: var(--call-minimized-bar-height, 0px) !important;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: calc(var(--toolbar-height, 78px) + var(--composer-height, 0px));
|
bottom: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9650,55 +9640,85 @@ body.chat-topbar-overlay .composer-slot > * {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Filters return to the same content offset they had before the toolbar-overlay experiment. */
|
/* Фильтры остаются ниже интерактивной части topbar. */
|
||||||
.network-stage > .fg-filter-bar.app-top-tabs {
|
.network-stage > .fg-filter-bar.app-top-tabs {
|
||||||
top: var(--app-primary-tabs-top-gap, 14px) !important;
|
top: calc(var(--topbar-height, 64px) + var(--app-primary-tabs-top-gap, 14px)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fade belongs to the graph surface, not to the toolbar. It sits above graph
|
/* Верхняя панель: зеркальная пара к нижней. Граф полностью скрыт примерно
|
||||||
* nodes/edges but below filter chips (filter bar z-index is 11). */
|
* в последней трети пути к верхнему краю, без blur и без отдельной плашки. */
|
||||||
.network-stage::before {
|
.app-shell:has(.screen-content.network-scroll-lock) .topbar-slot::before {
|
||||||
content: '';
|
height: 100%;
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 58px;
|
|
||||||
z-index: 10;
|
|
||||||
pointer-events: none;
|
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
rgba(5, 7, 10, 1) 0%,
|
rgba(5, 7, 10, 1) 0%,
|
||||||
rgba(5, 7, 10, 0.86) 28%,
|
rgba(5, 7, 10, 1) 32%,
|
||||||
rgba(5, 7, 10, 0.48) 58%,
|
rgba(5, 7, 10, 0.94) 38%,
|
||||||
rgba(5, 7, 10, 0.16) 82%,
|
rgba(5, 7, 10, 0.72) 46%,
|
||||||
|
rgba(5, 7, 10, 0.46) 54%,
|
||||||
|
rgba(5, 7, 10, 0.22) 61%,
|
||||||
|
rgba(5, 7, 10, 0.08) 65%,
|
||||||
|
rgba(5, 7, 10, 0) 68%,
|
||||||
rgba(5, 7, 10, 0) 100%
|
rgba(5, 7, 10, 0) 100%
|
||||||
);
|
);
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
-webkit-backdrop-filter: none !important;
|
||||||
|
-webkit-mask-image: none !important;
|
||||||
|
mask-image: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Explicitly keep the Connections header on the canonical topbar contract.
|
/* Нижняя панель не имеет собственной рамки/стеклянной плашки: её фон формирует
|
||||||
* No network-only absolute positioning, dimensions or padding. */
|
* только затухание графа, поэтому toolbar визуально сливается с #05070A. */
|
||||||
.topbar-slot > .page-header.app-topbar-shell.network-topbar {
|
.app-shell:has(.screen-content.network-scroll-lock) .toolbar-slot {
|
||||||
position: relative !important;
|
z-index: 20;
|
||||||
inset: auto !important;
|
isolation: isolate;
|
||||||
width: 100% !important;
|
overflow: visible !important;
|
||||||
height: calc(var(--app-topbar-base-height) + env(safe-area-inset-top)) !important;
|
|
||||||
min-height: calc(var(--app-topbar-base-height) + env(safe-area-inset-top)) !important;
|
|
||||||
max-height: calc(var(--app-topbar-base-height) + env(safe-area-inset-top)) !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: env(safe-area-inset-top) 0 0 !important;
|
|
||||||
display: grid !important;
|
|
||||||
grid-template-columns: minmax(44px, 1fr) minmax(0, auto) minmax(44px, 1fr) !important;
|
|
||||||
align-items: center !important;
|
|
||||||
gap: 8px !important;
|
|
||||||
border: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
|
border: 0 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Низ — точное зеркало верхней alpha-кривой. Граф полностью пропадает на ~68%
|
||||||
|
* высоты бара, поэтому оставшаяся примерно треть до нижнего края — чистый фон. */
|
||||||
|
.app-shell:has(.screen-content.network-scroll-lock) .toolbar-slot::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(5, 7, 10, 0) 0%,
|
||||||
|
rgba(5, 7, 10, 0) 32%,
|
||||||
|
rgba(5, 7, 10, 0.08) 35%,
|
||||||
|
rgba(5, 7, 10, 0.22) 39%,
|
||||||
|
rgba(5, 7, 10, 0.46) 46%,
|
||||||
|
rgba(5, 7, 10, 0.72) 54%,
|
||||||
|
rgba(5, 7, 10, 0.94) 62%,
|
||||||
|
rgba(5, 7, 10, 1) 68%,
|
||||||
|
rgba(5, 7, 10, 1) 100%
|
||||||
|
);
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
-webkit-backdrop-filter: none !important;
|
||||||
|
-webkit-mask-image: none !important;
|
||||||
|
mask-image: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell:has(.screen-content.network-scroll-lock) .toolbar-slot > .toolbar {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
background: transparent !important;
|
||||||
|
border: 0 !important;
|
||||||
|
border-color: transparent !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
backdrop-filter: none !important;
|
backdrop-filter: none !important;
|
||||||
-webkit-backdrop-filter: none !important;
|
-webkit-backdrop-filter: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ===== Единая полировка меню, DM и управляющих кнопок (2026-08-26) ===== */
|
/* ===== Единая полировка меню, DM и управляющих кнопок (2026-08-26) ===== */
|
||||||
|
|
||||||
/* Центральная вкладка остаётся на прежней оси иконок, но имеет большую
|
/* Центральная вкладка остаётся на прежней оси иконок, но имеет большую
|
||||||
@@ -9988,7 +10008,7 @@ body.chat-topbar-overlay .composer-slot {
|
|||||||
width: 42px;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
border: 3px solid rgba(220, 232, 255, 0.18);
|
border: 3px solid rgba(220, 232, 255, 0.18);
|
||||||
border-top-color: rgba(238, 203, 126, 0.95);
|
border-top-color: transparent;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: secret-generation-spin 0.82s linear infinite;
|
animation: secret-generation-spin 0.82s linear infinite;
|
||||||
}
|
}
|
||||||
@@ -10608,3 +10628,63 @@ body.chat-topbar-overlay .composer-slot {
|
|||||||
.profile-list-row-text{display:flex;flex-direction:column;gap:3px;min-width:0}.profile-list-row-text small{opacity:.68}
|
.profile-list-row-text{display:flex;flex-direction:column;gap:3px;min-width:0}.profile-list-row-text small{opacity:.68}
|
||||||
.profile-bottom-actions{justify-content:center;gap:12px;position:sticky;bottom:12px;z-index:3}
|
.profile-bottom-actions{justify-content:center;gap:12px;position:sticky;bottom:12px;z-index:3}
|
||||||
.profile-about:empty{display:none}
|
.profile-about:empty{display:none}
|
||||||
|
|
||||||
|
/* ===== «Связи»: боковой fade, ослабленный в зоне центрального света =====
|
||||||
|
* Радиальная виньетка давала слишком общий эффект. Здесь края затухают независимо:
|
||||||
|
* по X — узкой мягкой полосой от левого/правого края, а по Y сила этой полосы
|
||||||
|
* уменьшается возле источника света графа (50% / 47%) и растёт к углам.
|
||||||
|
* Поэтому центральное свечение визуально доходит почти до боковой кромки, тогда как
|
||||||
|
* верхние/нижние участки графа растворяются по краям раньше. Blur не используется. */
|
||||||
|
.app-shell:has(.screen-content.network-scroll-lock) .network-stage::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 10;
|
||||||
|
pointer-events: none;
|
||||||
|
background:
|
||||||
|
linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(5, 7, 10, 0.96) 0%,
|
||||||
|
rgba(5, 7, 10, 0.72) 4%,
|
||||||
|
rgba(5, 7, 10, 0.42) 8%,
|
||||||
|
rgba(5, 7, 10, 0.18) 12%,
|
||||||
|
rgba(5, 7, 10, 0.06) 15%,
|
||||||
|
rgba(5, 7, 10, 0) 18%
|
||||||
|
) left center / 50% 100% no-repeat,
|
||||||
|
linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgba(5, 7, 10, 0.96) 0%,
|
||||||
|
rgba(5, 7, 10, 0.72) 4%,
|
||||||
|
rgba(5, 7, 10, 0.42) 8%,
|
||||||
|
rgba(5, 7, 10, 0.18) 12%,
|
||||||
|
rgba(5, 7, 10, 0.06) 15%,
|
||||||
|
rgba(5, 7, 10, 0) 18%
|
||||||
|
) right center / 50% 100% no-repeat;
|
||||||
|
-webkit-mask-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(0, 0, 0, 0.96) 0%,
|
||||||
|
rgba(0, 0, 0, 0.76) 18%,
|
||||||
|
rgba(0, 0, 0, 0.46) 34%,
|
||||||
|
rgba(0, 0, 0, 0.18) 47%,
|
||||||
|
rgba(0, 0, 0, 0.28) 58%,
|
||||||
|
rgba(0, 0, 0, 0.54) 72%,
|
||||||
|
rgba(0, 0, 0, 0.82) 88%,
|
||||||
|
rgba(0, 0, 0, 0.96) 100%
|
||||||
|
);
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(0, 0, 0, 0.96) 0%,
|
||||||
|
rgba(0, 0, 0, 0.76) 18%,
|
||||||
|
rgba(0, 0, 0, 0.46) 34%,
|
||||||
|
rgba(0, 0, 0, 0.18) 47%,
|
||||||
|
rgba(0, 0, 0, 0.28) 58%,
|
||||||
|
rgba(0, 0, 0, 0.54) 72%,
|
||||||
|
rgba(0, 0, 0, 0.82) 88%,
|
||||||
|
rgba(0, 0, 0, 0.96) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DM header: human name is primary, login remains secondary. */
|
||||||
|
.chat-header-login-btn{display:flex;flex-direction:column;align-items:flex-start;line-height:1.1}
|
||||||
|
.chat-header-display-name{font-size:14px;font-weight:650}
|
||||||
|
.chat-header-user-login{font-size:11px;font-weight:400;opacity:.68;margin-top:2px}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ body::before {
|
|||||||
--toolbar-height: 78px;
|
--toolbar-height: 78px;
|
||||||
--keyboard-offset: 0px;
|
--keyboard-offset: 0px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-left: 1px solid rgba(211, 170, 86, 0.2);
|
border-left: 1px solid transparent;
|
||||||
border-right: 1px solid rgba(211, 170, 86, 0.2);
|
border-right: 1px solid transparent;
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ body.chat-topbar-overlay .app-shell.keyboard-open .screen-content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.connection-retry-banner.is-connecting {
|
.connection-retry-banner.is-connecting {
|
||||||
border-color: rgba(238, 196, 107, 0.42);
|
border-color: transparent;
|
||||||
color: #ffe8bb;
|
color: #ffe8bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -649,6 +649,6 @@
|
|||||||
|
|
||||||
/* «Общая связь» (этот человек — и твой друг тоже): золотой ободок. Значок ★ убран по запросу. */
|
/* «Общая связь» (этот человек — и твой друг тоже): золотой ободок. Значок ★ убран по запросу. */
|
||||||
.fg-node.is-common .node-dot {
|
.fg-node.is-common .node-dot {
|
||||||
border-color: rgba(255, 214, 120, 0.95);
|
border-color: transparent;
|
||||||
box-shadow: 0 0 14px rgba(255, 200, 90, 0.4);
|
box-shadow: 0 0 14px rgba(255, 200, 90, 0.4);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user