From 652ddc9d882c09f9a735fdb8c2ef92e44294b73a2e583e884d19294ab7a46a81 Mon Sep 17 00:00:00 2001 From: Pixel Date: Fri, 12 Jun 2026 14:10:06 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B2=D1=8F=D0=B7=D0=B8=20(test=2012.06)?= =?UTF-8?q?:=20SVG-=D1=81=D1=82=D0=B5=D0=BA=D0=BB=D1=8F=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=80=D0=B1=D1=8B=20=D0=B0=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=B0=D1=80=D0=BE=D0=B2=20+=20=D1=86=D0=B2=D0=B5=D1=82/=D1=81?= =?UTF-8?q?=D0=B2=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=B8=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B3=D0=BB=D1=83=D0=B1=D0=BE=D0=BA=D0=B8=D1=85?= =?UTF-8?q?=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Аватары → SVG GlassOrb (фото в стеклянной сфере, блик, rim, свечение). Линии глубоких связей (tier-2/3) — в цвете типа (друзья/семья/...), сияющие связи светятся (голубой ореол + ядро). Версия 1.2.159. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION.properties | 4 +- shine-UI/js/pages/network/force-graph.js | 80 +++++++++++++++++++----- shine-UI/styles/network-graph.css | 19 ++++++ 3 files changed, 86 insertions(+), 17 deletions(-) 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);