diff --git a/package-lock.json b/package-lock.json index 2d23eb6..11f6170 100644 --- a/package-lock.json +++ b/package-lock.json @@ -565,6 +565,7 @@ "resolved": "https://registry.npmjs.org/@data-fair/lib-node/-/lib-node-2.12.1.tgz", "integrity": "sha512-qNQwLV1XbzrRs/UlhHWWJRArenIy7qdg9JKuWK/NPnlLAJAzTvWkiOM/Q2UVnFo2CquJ4+7uUMymhEt7+nBw+Q==", "license": "MIT", + "peer": true, "dependencies": { "@data-fair/lib-common-types": "^1.8.4", "@data-fair/lib-utils": "^1.1.0", @@ -665,10 +666,11 @@ } }, "node_modules/@data-fair/lib-vue": { - "version": "1.27.1", - "resolved": "https://registry.npmjs.org/@data-fair/lib-vue/-/lib-vue-1.27.1.tgz", - "integrity": "sha512-vpUu7GXvdy3fNYiX6Y/heka+FBLrltcy/9ksCfVn0yTsOiXrS3tFwaD3RUMQUF+Q72KsGNI47A6TN9fgpOb/5w==", + "version": "1.28.1", + "resolved": "https://registry.npmjs.org/@data-fair/lib-vue/-/lib-vue-1.28.1.tgz", + "integrity": "sha512-lR7KHcc79bXGs3lIn3n/bzrLd0kZLnGwX1nSIKixOzcXpQP4AThxAVwE/+aZk/Y01VYhiGKwbIIrQzEsoWYAKg==", "license": "MIT", + "peer": true, "dependencies": { "@data-fair/lib-common-types": "^1.7.1", "@data-fair/lib-utils": "^1.9.0", @@ -677,6 +679,7 @@ "universal-cookie": "^7.2.0" }, "peerDependencies": { + "@vueuse/core": ">=10", "dayjs": "1", "ofetch": "1", "reconnecting-websocket": "4", @@ -684,6 +687,9 @@ "vue-router": "4 || 5" }, "peerDependenciesMeta": { + "@vueuse/core": { + "optional": true + }, "dayjs": { "optional": true }, @@ -758,6 +764,7 @@ "version": "1.9.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -769,6 +776,7 @@ "version": "1.9.2", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1780,6 +1788,7 @@ "resolved": "https://registry.npmjs.org/@json-layout/vocabulary/-/vocabulary-2.13.1.tgz", "integrity": "sha512-DYEPXu4MmPJkok5wOnezKIGM3tLU2iuyQ+YaiLw6j6RFiY2iBJapOIBVV3+BTiAeH+dTbn95Glf/W12tv9OS0w==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.17.1", "ajv-errors": "^3.0.0", @@ -2572,6 +2581,7 @@ "integrity": "sha512-PNRHbydNG5EH8NK4c+izdJlxajIR6GxcUhzsYNRsn6Myep4dsZt0qFCz3rCPnkvgO5FYibDcMqgNHUT+zvjYZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/utils": "^8.13.0", "eslint-visitor-keys": "^4.2.0", @@ -2749,6 +2759,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -2906,6 +2917,7 @@ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", @@ -3644,6 +3656,7 @@ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.9.0.tgz", "integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==", "license": "MIT", + "peer": true, "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.9.0", @@ -3720,6 +3733,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3753,6 +3767,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4085,6 +4100,7 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", @@ -4128,6 +4144,7 @@ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "license": "Apache-2.0", + "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -4680,6 +4697,7 @@ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -4856,7 +4874,8 @@ "version": "1.11.20", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -4951,8 +4970,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/detect-libc": { "version": "2.1.2", @@ -5399,6 +5417,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5557,6 +5576,7 @@ "integrity": "sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@package-json/types": "^0.0.12", "@typescript-eslint/types": "^8.56.0", @@ -8539,8 +8559,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/nodemailer": { "version": "8.0.5", @@ -9411,7 +9430,8 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz", "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", @@ -10929,6 +10949,7 @@ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "devOptional": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "tldts": "^6.1.32" }, @@ -11156,6 +11177,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11542,6 +11564,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -11644,6 +11667,7 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.31", "@vue/compiler-sfc": "3.5.31", @@ -11690,6 +11714,7 @@ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.3.0.tgz", "integrity": "sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA==", "license": "MIT", + "peer": true, "dependencies": { "@intlify/core-base": "11.3.0", "@intlify/devtools-types": "11.3.0", @@ -11711,6 +11736,7 @@ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.4.tgz", "integrity": "sha512-lCqDLCI2+fKVRl2OzXuzdSWmxXFLQRxQbmHugnRpTMyYiT+hNaycV0faqG5FBHDXoYrZ6MQcX87BvbY8mQ20Bg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/generator": "^7.28.6", "@vue-macros/common": "^3.1.1", @@ -11839,6 +11865,7 @@ "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.0.4.tgz", "integrity": "sha512-sO2ux9RG0C1HKaP1HqDMro3+vbbmUJwzcKXuzaxQmUERAT/0FR0yfbwnj4PrMwWy1qc2WPJq01h4cr86FmNrFA==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/johnleider" @@ -12193,7 +12220,7 @@ "dependencies": { "@data-fair/frame": "^0.18.4", "@data-fair/lib-utils": "^1.8.0", - "@data-fair/lib-vue": "^1.27.1", + "@data-fair/lib-vue": "^1.28.1", "@data-fair/lib-vuetify": "^2.0.5", "@data-fair/processings-shared": "*", "@intlify/unplugin-vue-i18n": "^11.0.7", diff --git a/ui/package.json b/ui/package.json index a974e24..792ef8e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -18,7 +18,7 @@ "dependencies": { "@data-fair/frame": "^0.18.4", "@data-fair/lib-utils": "^1.8.0", - "@data-fair/lib-vue": "^1.27.1", + "@data-fair/lib-vue": "^1.28.1", "@data-fair/lib-vuetify": "^2.0.5", "@data-fair/processings-shared": "*", "@intlify/unplugin-vue-i18n": "^11.0.7", diff --git a/ui/src/composables/use-follow-bottom.ts b/ui/src/composables/use-follow-bottom.ts deleted file mode 100644 index d44c631..0000000 --- a/ui/src/composables/use-follow-bottom.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useScroll, useEventListener } from '@vueuse/core' - -/** - * Stick-to-bottom autoscroll for a live run log: follows new entries while the - * user is at the bottom, any upward scroll or wheel-up stops it, scrolling back - * to the bottom resumes. - * - * Targets the current document — what actually scrolls both standalone and - * inside data-fair's fixed-height d-frame iframe (never window.top). VueUse's - * `arrivedState.bottom` carries a built-in 1px tolerance, and appending a log - * fires no scroll event, so following only ever turns off on a real upward - * scroll — no manual threshold needed. - * - * @param logCount reactive getter for the log length (the growth signal) - * @param isActive getter telling whether the run is still streaming - */ -export const useFollowBottom = (logCount: () => number, isActive: () => boolean) => { - // Start following so a freshly opened, still-running run pins to its tail even - // though the page loads scrolled to the top. - const following = ref(true) - - const { arrivedState, directions, y } = useScroll(window, { - onScroll: () => { - if (directions.top) following.value = false // scrolled up → stop - else if (arrivedState.bottom) following.value = true // back at bottom → resume - } - }) - - // A wheel-up reaches us even when the page can't scroll (short page, or - // data-fair's auto-height embed where the parent scrolls) — the only - // "stop following" signal available there. - useEventListener(window, 'wheel', (e: WheelEvent) => { if (e.deltaY < 0) following.value = false }, { passive: true }) - - const pinToBottom = () => { y.value = (document.scrollingElement ?? document.documentElement).scrollHeight } - - watch(logCount, () => { if (isActive() && following.value) pinToBottom() }, { flush: 'post' }) - - return { following } -} diff --git a/ui/src/pages/processings/[id]/runs/[runId].vue b/ui/src/pages/processings/[id]/runs/[runId].vue index dc451fb..ef787de 100644 --- a/ui/src/pages/processings/[id]/runs/[runId].vue +++ b/ui/src/pages/processings/[id]/runs/[runId].vue @@ -61,7 +61,7 @@