From 898addae87433e4dd13e984b0babad397247318f Mon Sep 17 00:00:00 2001 From: limina1 Date: Thu, 19 Dec 2024 15:54:42 -0500 Subject: [PATCH] fix eventMap redrawing & re-add mouseovertext --- pnpm-lock.yaml | 336 +++++++++++++++++++++++++ src/app.css | 8 +- src/lib/components/EventNetwork.svelte | 182 +++++++++----- 3 files changed, 458 insertions(+), 68 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34c8075..b76bcde 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: asciidoctor: specifier: ^3.0.4 version: 3.0.4(chokidar@3.6.0) + d3: + specifier: ^7.9.0 + version: 7.9.0 he: specifier: ^1.2.0 version: 1.2.0 @@ -57,6 +60,9 @@ importers: '@sveltejs/adapter-auto': specifier: ^3.1.1 version: 3.2.4(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4))) + '@sveltejs/adapter-static': + specifier: ^3.0.6 + version: 3.0.6(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4))) '@sveltejs/kit': specifier: ^2.4.3 version: 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)) @@ -538,6 +544,11 @@ packages: peerDependencies: '@sveltejs/kit': ^2.0.0 + '@sveltejs/adapter-static@3.0.6': + resolution: {integrity: sha512-MGJcesnJWj7FxDcB/GbrdYD3q24Uk0PIL4QIX149ku+hlJuj//nxUbb0HxUTpjkecWfHjVveSUnUaQWnPRXlpg==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + '@sveltejs/kit@2.5.25': resolution: {integrity: sha512-5hBSEN8XEjDZ5+2bHkFh8Z0QyOk0C187cyb12aANe1c8aeKbfu5ZD5XaC2vEH4h0alJFDXPdUkXQBmeeXeMr1A==} engines: {node: '>=18.13'} @@ -796,6 +807,10 @@ packages: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -823,6 +838,133 @@ packages: engines: {node: '>=4'} hasBin: true + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + d@1.0.2: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} @@ -859,6 +1001,9 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1228,6 +1373,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1250,6 +1399,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1802,6 +1955,9 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rollup@4.21.2: resolution: {integrity: sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1810,10 +1966,16 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sander@0.5.1: resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} @@ -2569,6 +2731,10 @@ snapshots: '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)) import-meta-resolve: 4.1.0 + '@sveltejs/adapter-static@3.0.6(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))': + dependencies: + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)) + '@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)))(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4))': dependencies: '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.2(@types/node@22.5.4)) @@ -2854,6 +3020,8 @@ snapshots: commander@5.1.0: {} + commander@7.2.0: {} + commander@9.5.0: {} concat-map@0.0.1: {} @@ -2878,6 +3046,158 @@ snapshots: cssesc@3.0.0: {} + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + d@1.0.2: dependencies: es5-ext: 0.10.64 @@ -2903,6 +3223,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.0.1 + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + dequal@2.0.3: {} detect-indent@6.1.0: {} @@ -3355,6 +3679,10 @@ snapshots: human-signals@2.1.0: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} import-fresh@3.3.0: @@ -3373,6 +3701,8 @@ snapshots: inherits@2.0.4: {} + internmap@2.0.3: {} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -3873,6 +4203,8 @@ snapshots: dependencies: glob: 7.2.3 + robust-predicates@3.0.2: {} + rollup@4.21.2: dependencies: '@types/estree': 1.0.5 @@ -3899,10 +4231,14 @@ snapshots: dependencies: queue-microtask: 1.2.3 + rw@1.3.3: {} + sade@1.8.1: dependencies: mri: 1.2.0 + safer-buffer@2.1.2: {} + sander@0.5.1: dependencies: es6-promise: 3.3.1 diff --git a/src/app.css b/src/app.css index a0ea8e8..8a26f05 100644 --- a/src/app.css +++ b/src/app.css @@ -150,8 +150,10 @@ .network-link-leather { @apply stroke-gray-400 dark:stroke-gray-600 fill-gray-400 dark:fill-gray-600; } - - .text-leather { - @apply fill-gray-800 dark:fill-gray-300; + .network-node-leather { + @apply stroke-gray-800; + } + .network-node-content { + @apply fill-[#d6c1a8]; } } diff --git a/src/lib/components/EventNetwork.svelte b/src/lib/components/EventNetwork.svelte index eb232fb..920f977 100644 --- a/src/lib/components/EventNetwork.svelte +++ b/src/lib/components/EventNetwork.svelte @@ -8,19 +8,18 @@ let svg: SVGSVGElement; let isDarkMode = false; const nodeRadius = 20; - const dragRadius = 45; - const linkDistance = 120; + const dragRadius = 10; + const linkDistance = 5; + const warmupClickEnergy = 0.9; // Energy to restart simulation on drag let container: HTMLDivElement; + let width: number; let height: number; - // Reactive statement for container dimensions $: if (container) { width = container.clientWidth || 800; height = container.clientHeight || 600; } - - // Type definitions for network components interface NetworkNode extends d3.SimulationNodeDatum { id: string; event?: NDKEvent; @@ -41,8 +40,6 @@ target: NetworkNode; isSequential: boolean; } - - // Create an efficient event map for O(1) lookups function createEventMap(events: NDKEvent[]): Map { return new Map(events.map((event) => [event.id, event])); } @@ -74,7 +71,9 @@ function getEventColor(eventId: string): string { const num = parseInt(eventId.slice(0, 4), 16); const hue = num % 360; - return `hsl(${hue}, 70%, 75%)`; + const saturation = 70; + const lightness = 75; + return `hsl(${hue}, ${saturation}%, ${lightness}%)`; } function generateGraph(events: NDKEvent[]): { @@ -84,6 +83,8 @@ const nodes: NetworkNode[] = []; const links: NetworkLink[] = []; const nodeMap = new Map(); + + // Create event lookup map - O(n) operation done once const eventMap = createEventMap(events); const indexEvents = events.filter((e) => e.kind === 30040); @@ -99,7 +100,7 @@ contentRefs.forEach((tag, idx) => { if (!tag[1]) return; - // Use O(1) lookup instead of O(n) find operation + // O(1) lookup instead of O(n) search const targetEvent = eventMap.get(tag[1]); if (!targetEvent) return; @@ -123,9 +124,8 @@ return { nodes, links }; } - function setupDragHandlers( - simulation: d3.Simulation, + simulation: d3.Simulation, ) { // Create drag behavior with proper typing const dragBehavior = d3 @@ -137,7 +137,8 @@ d: NetworkNode, ) => { // Warm up simulation when drag starts - if (!event.active) simulation.alphaTarget(0.3).restart(); + if (!event.active) + simulation.alphaTarget(warmupClickEnergy).restart(); // Fix node position during drag d.fx = d.x; d.fy = d.y; @@ -167,10 +168,8 @@ d.fy = null; }, ); - return dragBehavior; } - function drawNetwork() { if (!svg || !events?.length) return; @@ -178,22 +177,21 @@ if (!nodes.length) return; const svgElement = d3.select(svg).attr("viewBox", `0 0 ${width} ${height}`); + // Set up zoom behavior + let g = svgElement.append("g"); + + const zoom = d3 + .zoom() + .scaleExtent([0.4, 9]) + .on("zoom", (event) => { + g.attr("transform", event.transform); + }); - let g = svgElement.select("g"); - - // Only create the base group and zoom behavior if it doesn't exist + svgElement.call(zoom); if (g.empty()) { - const zoom = d3 - .zoom() - .scaleExtent([0.1, 4]) - .on("zoom", (event) => { - g.attr("transform", event.transform); - }); - - svgElement.call(zoom); g = svgElement.append("g"); - // Define arrow marker only once + // Define arrow marker with black fill const marker = g .append("defs") .selectAll("marker") @@ -210,10 +208,9 @@ marker .append("path") .attr("d", "M -8,-5 L 0, 0 L -8, 5 Z") - .attr("class", "network-link-leather"); + .attr("class", "network-link-leather"); // Black fill for arrowhead } - - // Set up force simulation + // Force simulation setup const simulation = d3 .forceSimulation(nodes) .force( @@ -223,16 +220,17 @@ .id((d) => d.id) .distance(linkDistance), ) - .force("charge", d3.forceManyBody().strength(-500)) + .force("charge", d3.forceManyBody().strength(-1000)) .force("center", d3.forceCenter(width / 2, height / 2)) - .force("x", d3.forceX(width / 2).strength(0.1)) - .force("y", d3.forceY(height / 2).strength(0.1)) - .force("collision", d3.forceCollide().radius(nodeRadius * 2.5)); - - // Create drag handler + .force("x", d3.forceX(width / 2).strength(0.1)) + .force("y", d3.forceY(height / 2).strength(0.1)) + .force( + "collision", + d3.forceCollide().radius(nodeRadius * 2.5), + ); const dragHandler = setupDragHandlers(simulation); - // Update links with enter/update/exit pattern + // Create links const link = g .selectAll("path.link") .data(links, (d: NetworkLink) => `${d.source.id}-${d.target.id}`) @@ -240,15 +238,15 @@ (enter) => enter .append("path") - .attr("class", "network-link-leather link") + .attr("class", "network-link-leather") .attr("stroke-width", 2) - .attr("fill", "none") + .attr("fill", "transparent") .attr("marker-end", "url(#arrowhead)"), (update) => update, (exit) => exit.remove(), ); - // Update nodes with enter/update/exit pattern + // Create nodes const node = g .selectAll("g.node") .data(nodes, (d: NetworkNode) => d.id) @@ -259,28 +257,33 @@ .attr("class", "node network-node-leather") .call(dragHandler); - // Add drag circle + // add drag circle nodeEnter .append("circle") - .attr("r", dragRadius) + .attr("r", nodeRadius * 2.5) .attr("fill", "transparent") + .attr("stroke", "transparent") .style("cursor", "move"); - // Add visible node circle + // add visual circle, stroke based on current theme nodeEnter .append("circle") .attr("r", nodeRadius) - .attr("stroke", "#000000") + .attr("class", (d: NetworkNode) => + !d.isContainer + ? "network-node-leather network-node-content" + : "network-node-leather", + ) .attr("stroke-width", 2); - // Add text labels + // add text labels nodeEnter .append("text") .attr("dy", "0.35em") .attr("text-anchor", "middle") - .attr("fill", "#000000") - .attr("font-size", "12px") - .attr("font-weight", "bold"); + .attr("fill", "black") + .attr("font-size", "12px"); + // .attr("font-weight", "bold"); return nodeEnter; }, @@ -288,39 +291,88 @@ (exit) => exit.remove(), ); - // Update node appearances + // Add text labels node .select("circle:nth-child(2)") - .attr("fill", (d) => + .attr("fill", (d: NetworkNode) => !d.isContainer ? isDarkMode - ? "#342718" - : "#d6c1a8" + ? "#FFFFFF" + : "network-link-leather" : getEventColor(d.id), ); - node.select("text").text((d) => (d.isContainer ? "I" : "C")); + node.select("text").text((d: NetworkNode) => (d.isContainer ? "I" : "C")); + // Add tooltips + const tooltip = d3 + .select("body") + .append("div") + .attr( + "class", + "fixed hidden bg-primary-0 dark:bg-primary-1000 " + + "text-gray-800 dark:text-gray-300 " + + "p-4 rounded shadow-lg border border-gray-200 dark:border-gray-800 " + + "transition-colors duration-200", + ) + .style("z-index", 1000); + + node + .on("mouseover", function (event, d) { + tooltip + .style("display", "block") + .html( + ` +
+
${d.title}
+
+ ${d.type} (${d.isContainer ? "30040" : "30041"}) +
+
+ ID: ${d.id} +
+ ${ + d.content + ? ` +
+ ${d.content} +
+ ` + : "" + } +
+ `, + ) + .style("left", event.pageX - 10 + "px") + .style("top", event.pageY + 10 + "px"); + }) + .on("mousemove", function (event) { + tooltip + .style("left", event.pageX + 10 + "px") + .style("top", event.pageY - 10 + "px"); + }) + .on("mouseout", () => { + tooltip.style("display", "none"); + }); - // Handle simulation updates + // Handle simulation ticks simulation.on("tick", () => { - // Update link positions link.attr("d", (d) => { const dx = d.target.x! - d.source.x!; const dy = d.target.y! - d.source.y!; const angle = Math.atan2(dy, dx); + + // Adjust start and end points to prevent overlap with nodes const startX = d.source.x! + nodeRadius * Math.cos(angle); const startY = d.source.y! + nodeRadius * Math.sin(angle); const endX = d.target.x! - nodeRadius * Math.cos(angle); const endY = d.target.y! - nodeRadius * Math.sin(angle); + return `M${startX},${startY}L${endX},${endY}`; }); - - // Update node positions node.attr("transform", (d) => `translate(${d.x},${d.y})`); }); } - // Setup and cleanup onMount(() => { isDarkMode = document.body.classList.contains("dark"); @@ -337,13 +389,12 @@ }); }); - // Watch for container size changes const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { width = entry.contentRect.width; - height = entry.contentRect.height || width * 0.6; + height = entry.contentRect.height * 1 || width * 1.0; } - if (svg) drawNetwork(); + // if (svg) drawNetwork(); }); // Start observers @@ -352,15 +403,13 @@ attributeFilter: ["class"], }); resizeObserver.observe(container); - - // Cleanup function + // Clean up return () => { themeObserver.disconnect(); resizeObserver.disconnect(); }; }); - - // Reactive redraw + // Reactive redaw $: { if (svg && events?.length) { drawNetwork(); @@ -369,7 +418,7 @@ # /lib/components/EventNetwork.svelte -
+
+ +