Переработка экрана «Связи» в интерактивный нод-граф с премиальными переходами. Движок (js/pages/network/force-graph.js): - diffing-переходы: общие узлы перелетают, новые расцветают каскадом, исчезнувшие — Ghost-слой (800мс, на месте); - мягкая радиальная пружина + отталкивание (органичная орбита), упругий влёт фокуса; - динамическая вязкость на старте (трение 0.92→0.82, отталкивание ослаблено) — мягкий разлёт без тряски; - жёсткая заморозка (kill-switch) при затухании — нет «треска», экономия батареи; - линии — SVG <path> Безье (изогнутые нити), прорастание; жесты pan с инерцией; - хард-лимит DOM-аватарок (остальное — SVG-точки). Интеграция и UX: - adapter.js: getUserConnectionsGraph → модель движка (сервер не трогаем, read-only); - фильтры (Все/Семья/Друзья/Сияющие), контекстное меню (node-menu.js), нижний сниппет, профиль; - прицел в центре, дыхание фокуса, свечение сияющих; - лаборатория network-view/lab на мок-данных (networkGraphUsers) для тестов без бэкенда. Документация: shine-UI/Dev_Docs/features/interactive-network-graph.md. Бамп client.version 1.2.135 -> 1.2.136. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
82 lines
3.1 KiB
HTML
82 lines
3.1 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
<link rel="manifest" href="./manifest.webmanifest" />
|
|
<title>Shine UI Demo</title>
|
|
<script>
|
|
window.__SHINE_BUILD_HASH__ = '20260530000700';
|
|
window.__SHINE_CLIENT_VERSION__ = '1.2.8';
|
|
</script>
|
|
<script>
|
|
(function attachStylesWithBuildHash() {
|
|
const v = encodeURIComponent(window.__SHINE_BUILD_HASH__ || 'dev');
|
|
const cssFiles = ['./styles/main.css', './styles/layout.css', './styles/components.css', './styles/network-graph.css'];
|
|
cssFiles.forEach((file) => {
|
|
const link = document.createElement('link');
|
|
link.rel = 'stylesheet';
|
|
link.href = `${file}?v=${v}`;
|
|
document.head.appendChild(link);
|
|
});
|
|
}());
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div class="app-shell">
|
|
<main id="app-screen" class="screen-content"></main>
|
|
<div id="toolbar-slot" class="toolbar-slot"></div>
|
|
</div>
|
|
<div id="modal-root"></div>
|
|
<script>
|
|
// Public VAPID key for Web Push (Base64URL)
|
|
window.__SHINE_WEBPUSH_VAPID_PUBLIC_KEY__ = 'BOdoWZndZRaNe9kyUFsJ5-xEfFABXNKennAKg15Z7ycAwUIQ7yDV_sIWWYJCwJriN4g9oU-CyJPrn1U6lfxuDbI';
|
|
</script>
|
|
<script>
|
|
(function attachBootErrorOverlay() {
|
|
const show = (title, text) => {
|
|
try {
|
|
let el = document.getElementById('boot-error-overlay');
|
|
if (!el) {
|
|
el = document.createElement('pre');
|
|
el.id = 'boot-error-overlay';
|
|
el.style.position = 'fixed';
|
|
el.style.left = '8px';
|
|
el.style.right = '8px';
|
|
el.style.bottom = '8px';
|
|
el.style.maxHeight = '40vh';
|
|
el.style.overflow = 'auto';
|
|
el.style.padding = '10px';
|
|
el.style.margin = '0';
|
|
el.style.background = 'rgba(120, 0, 0, 0.92)';
|
|
el.style.color = '#fff';
|
|
el.style.fontSize = '12px';
|
|
el.style.lineHeight = '1.4';
|
|
el.style.zIndex = '999999';
|
|
el.style.whiteSpace = 'pre-wrap';
|
|
document.body.appendChild(el);
|
|
}
|
|
el.textContent = `[BOOT ERROR] ${title}\n${String(text || '')}`;
|
|
} catch {}
|
|
};
|
|
window.addEventListener('error', (e) => {
|
|
show('window.error', `${e?.message || ''}\n${e?.filename || ''}:${e?.lineno || ''}:${e?.colno || ''}`);
|
|
});
|
|
window.addEventListener('unhandledrejection', (e) => {
|
|
const reason = e?.reason;
|
|
show('unhandledrejection', reason?.stack || reason?.message || String(reason || 'unknown'));
|
|
});
|
|
}());
|
|
</script>
|
|
<script>
|
|
(function attachAppWithBuildHash() {
|
|
const v = encodeURIComponent(window.__SHINE_BUILD_HASH__ || 'dev');
|
|
const script = document.createElement('script');
|
|
script.type = 'module';
|
|
script.src = `./js/app.js?v=${v}`;
|
|
document.body.appendChild(script);
|
|
}());
|
|
</script>
|
|
</body>
|
|
</html>
|