diff --git a/VERSION.properties b/VERSION.properties
index 985cb9b..3e565e0 100644
--- a/VERSION.properties
+++ b/VERSION.properties
@@ -1,2 +1,2 @@
-client.version=1.2.158
-server.version=1.2.142
+client.version=1.2.159
+server.version=1.2.143
diff --git a/shine-UI/js/pages/network/force-graph.js b/shine-UI/js/pages/network/force-graph.js
index 0f4dada..6dde968 100644
--- a/shine-UI/js/pages/network/force-graph.js
+++ b/shine-UI/js/pages/network/force-graph.js
@@ -419,6 +419,45 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
return wrap;
}
+ // SVG-«стеклянный орб» для аватара (фото в стеклянной сфере ≈90% + блик + rim + свечение).
+ // Уникальные id на экземпляр (иначе defs конфликтуют). Тело стекла тёмно-синее/прозрачное (без серости),
+ // вторичный блик убран (был «звёздочкой»). Фолбэк: если фото не загрузилось — остаются инициалы.
+ let orbSeq = 0;
+ function buildGlassOrb(src, opts) {
+ const o = opts || {};
+ const u = 'o' + (orbSeq += 1);
+ const glowOp = o.isFocus ? 0.34 : 0.28;
+ const imgFilter = o.isFocus ? 'grayscale(0.9) contrast(1.04)' : 'saturate(0.85) brightness(0.97)';
+ const init = String(o.initials || '').slice(0, 2);
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ svg.setAttribute('viewBox', '0 0 100 100');
+ svg.setAttribute('class', 'fg-orb-svg');
+ svg.setAttribute('aria-hidden', 'true');
+ svg.innerHTML = ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + (init ? '' + init + '' : '')
+ + (src ? '' : '')
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + '';
+ const im = svg.querySelector('image');
+ if (im) im.addEventListener('error', () => { try { im.remove(); } catch (e) { /* останутся инициалы */ } });
+ return svg;
+ }
+
function buildNodeElement(src, isFocus, tier, dotOnly = false) {
const el = document.createElement('button');
el.type = 'button';
@@ -447,17 +486,14 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
].filter(Boolean).join(' ');
el.dataset.nodeId = String(src.id);
- // тестовое фото (лаборатория) — прямой URL; иначе штатный аватар (Arweave/инициалы)
- const avatar = src.photo
- ? buildPhotoAvatar(src)
- : renderUserAvatar({
- login: src.login || src.name || String(src.id),
- firstName: src.name || '',
- avatar: src.avatar || null,
- size: 'node',
- title: src.name || src.login || '',
- });
- el.append(avatar);
+ // Аватар = SVG-«стеклянный орб» (фото в стеклянной сфере). Хост — .node-dot (масштаб/состояния/
+ // синхронизация позиций движка). Меняем ТОЛЬКО аватар; бейдж/имя/линии — как раньше.
+ const photoSrc = src.photo || (src.avatar && src.avatar !== 'url_to_image' ? src.avatar : null);
+ const initials = buildAvatarInitials({ login: src.login || src.name || String(src.id), firstName: src.name || '' });
+ const dot = document.createElement('div');
+ dot.className = 'avatar node-dot fg-orb-host';
+ dot.appendChild(buildGlassOrb(photoSrc, { isFocus, initials }));
+ el.append(dot);
// Бейдж-счётчик числа связей (заполняется в updateBadges по degreeById). Скрыт, пока 0.
const badge = document.createElement('span');
@@ -772,11 +808,25 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
}
const pe = parent.expandP || 0; // насколько раскрыт родитель (глубокие лучи видны вместе с детьми)
if (n.tier >= 3) {
- // 3-й уровень: микрозвезда — еле заметная космическая ниточка, видна только при раскрытии
- if (pe > 0.02) parts.push(``);
+ // 3-й уровень: тонкая нить В ЦВЕТЕ СВЯЗИ (видна при раскрытии). Сияющая — светится (ореол+ядро).
+ if (pe > 0.02) {
+ if (shine) {
+ parts.push(``);
+ parts.push(``);
+ } else {
+ parts.push(``);
+ }
+ }
} else if (n.tier === 2) {
- // 2-й уровень: матовая паутинка, проявляется по мере раскрытия родителя (локальный bloom)
- if (pe > 0.02) parts.push(``);
+ // 2-й уровень: связь В ЦВЕТЕ ТИПА (семья/друзья/...). Сияющая связь — светящаяся линия.
+ if (pe > 0.02) {
+ if (shine) {
+ parts.push(``);
+ parts.push(``);
+ } else {
+ parts.push(``);
+ }
+ }
} else if (shine || n.track || onPath) {
// СИЯЮЩАЯ связь — плазменный композитинг: ОДИН центральный S-путь (cubic) + ТРИ наложенных слоя
// с ОДИНАКОВЫМ d (объём из толщины+blur, не из геометрии). Слои: широкое поле / трубка / ядро.
diff --git a/shine-UI/styles/network-graph.css b/shine-UI/styles/network-graph.css
index 5350608..1ed7133 100644
--- a/shine-UI/styles/network-graph.css
+++ b/shine-UI/styles/network-graph.css
@@ -128,6 +128,25 @@
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}
+/* SVG-«стеклянный орб» — масштабируем так, чтобы сфера (r42 = 84% SVG) ≈ диаметр узла → линии-связи
+ прилипают к краю орба, как раньше. Хост .node-dot держит размер/состояния/синхронизацию позиций. */
+.fg-node .node-dot.fg-orb-host {
+ position: relative;
+ background: none;
+ overflow: visible; /* не срезать внешнее свечение орба */
+ box-shadow: none;
+}
+.fg-orb-svg {
+ position: absolute;
+ width: 119%;
+ height: 119%;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ display: block;
+ overflow: visible;
+}
+
.fg-node.is-family .node-dot {
background: linear-gradient(165deg, #785038, #5f3e2c);
border-color: rgba(255, 194, 143, 0.6);