Brunobkr/llama.cpp_AlgMor24_github
ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.
03.1k
1/**2 * This logic is adapted from Radix UI ScrollArea component.3 * https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/src/ScrollArea.tsx4 * Credit to Jenna Smith (@jjenzz) for the original implementation.5 * Incredible thought must have went into solving all the intricacies of this component.6 */7import { Context, useDebounce, watch } from "runed";8import { untrack } from "svelte";9import { simpleBox, executeCallbacks, attachRef, DOMContext, getWindow, } from "svelte-toolbelt";10import { mergeProps, useId } from "../../shared/index.js";11import { clamp } from "../../internal/clamp.js";12import { on } from "svelte/events";13import { createBitsAttrs } from "../../internal/attrs.js";14import { StateMachine } from "../../internal/state-machine.js";15import { SvelteResizeObserver } from "../../internal/svelte-resize-observer.svelte.js";16const scrollAreaAttrs = createBitsAttrs({17 component: "scroll-area",18 parts: ["root", "viewport", "corner", "thumb", "scrollbar"],19});20export const ScrollAreaRootContext = new Context("ScrollArea.Root");21export const ScrollAreaScrollbarContext = new Context("ScrollArea.Scrollbar");22export const ScrollAreaScrollbarVisibleContext = new Context("ScrollArea.ScrollbarVisible");23export const ScrollAreaScrollbarAxisContext = new Context("ScrollArea.ScrollbarAxis");24export const ScrollAreaScrollbarSharedContext = new Context("ScrollArea.ScrollbarShared");25export class ScrollAreaRootState {26 static create(opts) {27 return ScrollAreaRootContext.set(new ScrollAreaRootState(opts));28 }29 opts;30 attachment;31 scrollAreaNode = $state(null);32 viewportNode = $state(null);33 contentNode = $state(null);34 scrollbarXNode = $state(null);35 scrollbarYNode = $state(null);36 cornerWidth = $state(0);37 cornerHeight = $state(0);38 scrollbarXEnabled = $state(false);39 scrollbarYEnabled = $state(false);40 domContext;41 constructor(opts) {42 this.opts = opts;43 this.attachment = attachRef(opts.ref, (v) => (this.scrollAreaNode = v));44 this.domContext = new DOMContext(opts.ref);45 }46 props = $derived.by(() => ({47 id: this.opts.id.current,48 dir: this.opts.dir.current,49 style: {50 position: "relative",51 "--bits-scroll-area-corner-height": `${this.cornerHeight}px`,52 "--bits-scroll-area-corner-width": `${this.cornerWidth}px`,53 },54 [scrollAreaAttrs.root]: "",55 ...this.attachment,56 }));57}58export class ScrollAreaViewportState {59 static create(opts) {60 return new ScrollAreaViewportState(opts, ScrollAreaRootContext.get());61 }62 opts;63 root;64 attachment;65 #contentId = simpleBox(useId());66 #contentRef = simpleBox(null);67 contentAttachment = attachRef(this.#contentRef, (v) => (this.root.contentNode = v));68 constructor(opts, root) {69 this.opts = opts;70 this.root = root;71 this.attachment = attachRef(opts.ref, (v) => (this.root.viewportNode = v));72 }73 props = $derived.by(() => ({74 id: this.opts.id.current,75 style: {76 overflowX: this.root.scrollbarXEnabled ? "scroll" : "hidden",77 overflowY: this.root.scrollbarYEnabled ? "scroll" : "hidden",78 },79 [scrollAreaAttrs.viewport]: "",80 ...this.attachment,81 }));82 contentProps = $derived.by(() => ({83 id: this.#contentId.current,84 "data-scroll-area-content": "",85 /**86 * When horizontal scrollbar is visible: this element should be at least87 * as wide as its children for size calculations to work correctly.88 *89 * When horizontal scrollbar is NOT visible: this element's width should90 * be constrained by the parent container to enable `text-overflow: ellipsis`91 */92 style: { minWidth: this.root.scrollbarXEnabled ? "fit-content" : undefined },93 ...this.contentAttachment,94 }));95}96export class ScrollAreaScrollbarState {97 static create(opts) {98 return ScrollAreaScrollbarContext.set(new ScrollAreaScrollbarState(opts, ScrollAreaRootContext.get()));99 }100 opts;101 root;102 isHorizontal = $derived.by(() => this.opts.orientation.current === "horizontal");103 hasThumb = $state(false);104 constructor(opts, root) {105 this.opts = opts;106 this.root = root;107 watch(() => this.isHorizontal, (isHorizontal) => {108 if (isHorizontal) {109 this.root.scrollbarXEnabled = true;110 return () => {111 this.root.scrollbarXEnabled = false;112 };113 }114 else {115 this.root.scrollbarYEnabled = true;116 return () => {117 this.root.scrollbarYEnabled = false;118 };119 }120 });121 }122}123export class ScrollAreaScrollbarHoverState {124 static create() {125 return new ScrollAreaScrollbarHoverState(ScrollAreaScrollbarContext.get());126 }127 scrollbar;128 root;129 isVisible = $state(false);130 constructor(scrollbar) {131 this.scrollbar = scrollbar;132 this.root = scrollbar.root;133 $effect(() => {134 const scrollAreaNode = this.root.scrollAreaNode;135 const hideDelay = this.root.opts.scrollHideDelay.current;136 let hideTimer = 0;137 if (!scrollAreaNode)138 return;139 const handlePointerEnter = () => {140 this.root.domContext.clearTimeout(hideTimer);141 untrack(() => (this.isVisible = true));142 };143 const handlePointerLeave = () => {144 if (hideTimer)145 this.root.domContext.clearTimeout(hideTimer);146 hideTimer = this.root.domContext.setTimeout(() => {147 untrack(() => {148 this.scrollbar.hasThumb = false;149 this.isVisible = false;150 });151 }, hideDelay);152 };153 const unsubListeners = executeCallbacks(on(scrollAreaNode, "pointerenter", handlePointerEnter), on(scrollAreaNode, "pointerleave", handlePointerLeave));154 return () => {155 this.root.domContext.getWindow().clearTimeout(hideTimer);156 unsubListeners();157 };158 });159 }160 props = $derived.by(() => ({161 "data-state": this.isVisible ? "visible" : "hidden",162 }));163}164export class ScrollAreaScrollbarScrollState {165 static create() {166 return new ScrollAreaScrollbarScrollState(ScrollAreaScrollbarContext.get());167 }168 scrollbar;169 root;170 machine = new StateMachine("hidden", {171 hidden: {172 SCROLL: "scrolling",173 },174 scrolling: {175 SCROLL_END: "idle",176 POINTER_ENTER: "interacting",177 },178 interacting: {179 SCROLL: "interacting",180 POINTER_LEAVE: "idle",181 },182 idle: {183 HIDE: "hidden",184 SCROLL: "scrolling",185 POINTER_ENTER: "interacting",186 },187 });188 isHidden = $derived.by(() => this.machine.state.current === "hidden");189 constructor(scrollbar) {190 this.scrollbar = scrollbar;191 this.root = scrollbar.root;192 const debounceScrollend = useDebounce(() => this.machine.dispatch("SCROLL_END"), 100);193 $effect(() => {194 const _state = this.machine.state.current;195 const scrollHideDelay = this.root.opts.scrollHideDelay.current;196 if (_state === "idle") {197 const hideTimer = this.root.domContext.setTimeout(() => this.machine.dispatch("HIDE"), scrollHideDelay);198 return () => this.root.domContext.clearTimeout(hideTimer);199 }200 });201 $effect(() => {202 const viewportNode = this.root.viewportNode;203 if (!viewportNode)204 return;205 const scrollDirection = this.scrollbar.isHorizontal ? "scrollLeft" : "scrollTop";206 let prevScrollPos = viewportNode[scrollDirection];207 const handleScroll = () => {208 const scrollPos = viewportNode[scrollDirection];209 const hasScrollInDirectionChanged = prevScrollPos !== scrollPos;210 if (hasScrollInDirectionChanged) {211 this.machine.dispatch("SCROLL");212 debounceScrollend();213 }214 prevScrollPos = scrollPos;215 };216 const unsubListener = on(viewportNode, "scroll", handleScroll);217 return unsubListener;218 });219 this.onpointerenter = this.onpointerenter.bind(this);220 this.onpointerleave = this.onpointerleave.bind(this);221 }222 onpointerenter(_) {223 this.machine.dispatch("POINTER_ENTER");224 }225 onpointerleave(_) {226 this.machine.dispatch("POINTER_LEAVE");227 }228 props = $derived.by(() => ({229 "data-state": this.machine.state.current === "hidden" ? "hidden" : "visible",230 onpointerenter: this.onpointerenter,231 onpointerleave: this.onpointerleave,232 }));233}234export class ScrollAreaScrollbarAutoState {235 static create() {236 return new ScrollAreaScrollbarAutoState(ScrollAreaScrollbarContext.get());237 }238 scrollbar;239 root;240 isVisible = $state(false);241 constructor(scrollbar) {242 this.scrollbar = scrollbar;243 this.root = scrollbar.root;244 const handleResize = useDebounce(() => {245 const viewportNode = this.root.viewportNode;246 if (!viewportNode)247 return;248 const isOverflowX = viewportNode.offsetWidth < viewportNode.scrollWidth;249 const isOverflowY = viewportNode.offsetHeight < viewportNode.scrollHeight;250 this.isVisible = this.scrollbar.isHorizontal ? isOverflowX : isOverflowY;251 }, 10);252 new SvelteResizeObserver(() => this.root.viewportNode, handleResize);253 new SvelteResizeObserver(() => this.root.contentNode, handleResize);254 }255 props = $derived.by(() => ({256 "data-state": this.isVisible ? "visible" : "hidden",257 }));258}259export class ScrollAreaScrollbarVisibleState {260 static create() {261 return ScrollAreaScrollbarVisibleContext.set(new ScrollAreaScrollbarVisibleState(ScrollAreaScrollbarContext.get()));262 }263 scrollbar;264 root;265 thumbNode = $state(null);266 pointerOffset = $state(0);267 sizes = $state.raw({268 content: 0,269 viewport: 0,270 scrollbar: { size: 0, paddingStart: 0, paddingEnd: 0 },271 });272 thumbRatio = $derived.by(() => getThumbRatio(this.sizes.viewport, this.sizes.content));273 hasThumb = $derived.by(() => Boolean(this.thumbRatio > 0 && this.thumbRatio < 1));274 // this needs to be a $state to properly restore the transform style when the scrollbar275 // goes from a hidden to visible state, otherwise it will start at the beginning of the276 // scrollbar and flicker to the correct position after277 prevTransformStyle = $state("");278 constructor(scrollbar) {279 this.scrollbar = scrollbar;280 this.root = scrollbar.root;281 $effect(() => {282 this.scrollbar.hasThumb = this.hasThumb;283 });284 $effect(() => {285 if (!this.scrollbar.hasThumb && this.thumbNode) {286 this.prevTransformStyle = this.thumbNode.style.transform;287 }288 });289 }290 setSizes(sizes) {291 this.sizes = sizes;292 }293 getScrollPosition(pointerPos, dir) {294 return getScrollPositionFromPointer({295 pointerPos,296 pointerOffset: this.pointerOffset,297 sizes: this.sizes,298 dir,299 });300 }301 onThumbPointerUp() {302 this.pointerOffset = 0;303 }304 onThumbPointerDown(pointerPos) {305 this.pointerOffset = pointerPos;306 }307 xOnThumbPositionChange() {308 if (!(this.root.viewportNode && this.thumbNode))309 return;310 const scrollPos = this.root.viewportNode.scrollLeft;311 const offset = getThumbOffsetFromScroll({312 scrollPos,313 sizes: this.sizes,314 dir: this.root.opts.dir.current,315 });316 const transformStyle = `translate3d(${offset}px, 0, 0)`;317 this.thumbNode.style.transform = transformStyle;318 this.prevTransformStyle = transformStyle;319 }320 xOnWheelScroll(scrollPos) {321 if (!this.root.viewportNode)322 return;323 this.root.viewportNode.scrollLeft = scrollPos;324 }325 xOnDragScroll(pointerPos) {326 if (!this.root.viewportNode)327 return;328 this.root.viewportNode.scrollLeft = this.getScrollPosition(pointerPos, this.root.opts.dir.current);329 }330 yOnThumbPositionChange() {331 if (!(this.root.viewportNode && this.thumbNode))332 return;333 const scrollPos = this.root.viewportNode.scrollTop;334 const offset = getThumbOffsetFromScroll({ scrollPos, sizes: this.sizes });335 const transformStyle = `translate3d(0, ${offset}px, 0)`;336 this.thumbNode.style.transform = transformStyle;337 this.prevTransformStyle = transformStyle;338 }339 yOnWheelScroll(scrollPos) {340 if (!this.root.viewportNode)341 return;342 this.root.viewportNode.scrollTop = scrollPos;343 }344 yOnDragScroll(pointerPos) {345 if (!this.root.viewportNode)346 return;347 this.root.viewportNode.scrollTop = this.getScrollPosition(pointerPos, this.root.opts.dir.current);348 }349}350export class ScrollAreaScrollbarXState {351 static create(opts) {352 return ScrollAreaScrollbarAxisContext.set(new ScrollAreaScrollbarXState(opts, ScrollAreaScrollbarVisibleContext.get()));353 }354 opts;355 scrollbarVis;356 root;357 scrollbar;358 attachment;359 computedStyle = $state();360 constructor(opts, scrollbarVis) {361 this.opts = opts;362 this.scrollbarVis = scrollbarVis;363 this.root = scrollbarVis.root;364 this.scrollbar = scrollbarVis.scrollbar;365 this.attachment = attachRef(this.scrollbar.opts.ref, (v) => (this.root.scrollbarXNode = v));366 $effect(() => {367 if (!this.scrollbar.opts.ref.current)368 return;369 if (this.opts.mounted.current) {370 this.computedStyle = getComputedStyle(this.scrollbar.opts.ref.current);371 }372 });373 $effect(() => {374 // Ensure when a user scrolls down and then the scrollbar is hidden375 // that when it shows again it will be positioned correctly.376 this.onResize();377 });378 }379 onThumbPointerDown = (pointerPos) => {380 this.scrollbarVis.onThumbPointerDown(pointerPos.x);381 };382 onDragScroll = (pointerPos) => {383 this.scrollbarVis.xOnDragScroll(pointerPos.x);384 };385 onThumbPointerUp = () => {386 this.scrollbarVis.onThumbPointerUp();387 };388 onThumbPositionChange = () => {389 this.scrollbarVis.xOnThumbPositionChange();390 };391 onWheelScroll = (e, maxScrollPos) => {392 if (!this.root.viewportNode)393 return;394 const scrollPos = this.root.viewportNode.scrollLeft + e.deltaX;395 this.scrollbarVis.xOnWheelScroll(scrollPos);396 // prevent window scroll when wheeling scrollbar397 if (isScrollingWithinScrollbarBounds(scrollPos, maxScrollPos)) {398 e.preventDefault();399 }400 };401 onResize = () => {402 if (!(this.scrollbar.opts.ref.current && this.root.viewportNode && this.computedStyle))403 return;404 this.scrollbarVis.setSizes({405 content: this.root.viewportNode.scrollWidth,406 viewport: this.root.viewportNode.offsetWidth,407 scrollbar: {408 size: this.scrollbar.opts.ref.current.clientWidth,409 paddingStart: toInt(this.computedStyle.paddingLeft),410 paddingEnd: toInt(this.computedStyle.paddingRight),411 },412 });413 };414 thumbSize = $derived.by(() => {415 return getThumbSize(this.scrollbarVis.sizes);416 });417 props = $derived.by(() => ({418 id: this.scrollbar.opts.id.current,419 "data-orientation": "horizontal",420 style: {421 bottom: 0,422 left: this.root.opts.dir.current === "rtl"423 ? "var(--bits-scroll-area-corner-width)"424 : 0,425 right: this.root.opts.dir.current === "ltr"426 ? "var(--bits-scroll-area-corner-width)"427 : 0,428 "--bits-scroll-area-thumb-width": `${this.thumbSize}px`,429 },430 ...this.attachment,431 }));432}433export class ScrollAreaScrollbarYState {434 static create(opts) {435 return ScrollAreaScrollbarAxisContext.set(new ScrollAreaScrollbarYState(opts, ScrollAreaScrollbarVisibleContext.get()));436 }437 opts;438 scrollbarVis;439 root;440 scrollbar;441 attachment;442 computedStyle = $state();443 constructor(opts, scrollbarVis) {444 this.opts = opts;445 this.scrollbarVis = scrollbarVis;446 this.root = scrollbarVis.root;447 this.scrollbar = scrollbarVis.scrollbar;448 this.attachment = attachRef(this.scrollbar.opts.ref, (v) => (this.root.scrollbarYNode = v));449 $effect(() => {450 if (!this.scrollbar.opts.ref.current)451 return;452 if (this.opts.mounted.current) {453 this.computedStyle = getComputedStyle(this.scrollbar.opts.ref.current);454 }455 });456 $effect(() => {457 // Ensure when a user scrolls down and then the scrollbar is hidden458 // that when it shows again it will be positioned correctly.459 this.onResize();460 });461 this.onThumbPointerDown = this.onThumbPointerDown.bind(this);462 this.onDragScroll = this.onDragScroll.bind(this);463 this.onThumbPointerUp = this.onThumbPointerUp.bind(this);464 this.onThumbPositionChange = this.onThumbPositionChange.bind(this);465 this.onWheelScroll = this.onWheelScroll.bind(this);466 this.onResize = this.onResize.bind(this);467 }468 onThumbPointerDown(pointerPos) {469 this.scrollbarVis.onThumbPointerDown(pointerPos.y);470 }471 onDragScroll(pointerPos) {472 this.scrollbarVis.yOnDragScroll(pointerPos.y);473 }474 onThumbPointerUp() {475 this.scrollbarVis.onThumbPointerUp();476 }477 onThumbPositionChange() {478 this.scrollbarVis.yOnThumbPositionChange();479 }480 onWheelScroll(e, maxScrollPos) {481 if (!this.root.viewportNode)482 return;483 const scrollPos = this.root.viewportNode.scrollTop + e.deltaY;484 this.scrollbarVis.yOnWheelScroll(scrollPos);485 // prevent window scroll when wheeling scrollbar486 if (isScrollingWithinScrollbarBounds(scrollPos, maxScrollPos)) {487 e.preventDefault();488 }489 }490 onResize() {491 if (!(this.scrollbar.opts.ref.current && this.root.viewportNode && this.computedStyle))492 return;493 this.scrollbarVis.setSizes({494 content: this.root.viewportNode.scrollHeight,495 viewport: this.root.viewportNode.offsetHeight,496 scrollbar: {497 size: this.scrollbar.opts.ref.current.clientHeight,498 paddingStart: toInt(this.computedStyle.paddingTop),499 paddingEnd: toInt(this.computedStyle.paddingBottom),500 },501 });502 }503 thumbSize = $derived.by(() => {504 return getThumbSize(this.scrollbarVis.sizes);505 });506 props = $derived.by(() => ({507 id: this.scrollbar.opts.id.current,508 "data-orientation": "vertical",509 style: {510 top: 0,511 right: this.root.opts.dir.current === "ltr" ? 0 : undefined,512 left: this.root.opts.dir.current === "rtl" ? 0 : undefined,513 bottom: "var(--bits-scroll-area-corner-height)",514 "--bits-scroll-area-thumb-height": `${this.thumbSize}px`,515 },516 ...this.attachment,517 }));518}519export class ScrollAreaScrollbarSharedState {520 static create() {521 return ScrollAreaScrollbarSharedContext.set(new ScrollAreaScrollbarSharedState(ScrollAreaScrollbarAxisContext.get()));522 }523 scrollbarState;524 root;525 scrollbarVis;526 scrollbar;527 rect = $state.raw(null);528 prevWebkitUserSelect = $state("");529 handleResize;530 handleThumbPositionChange;531 handleWheelScroll;532 handleThumbPointerDown;533 handleThumbPointerUp;534 maxScrollPos = $derived.by(() => this.scrollbarVis.sizes.content - this.scrollbarVis.sizes.viewport);535 constructor(scrollbarState) {536 this.scrollbarState = scrollbarState;537 this.root = scrollbarState.root;538 this.scrollbarVis = scrollbarState.scrollbarVis;539 this.scrollbar = scrollbarState.scrollbarVis.scrollbar;540 this.handleResize = useDebounce(() => this.scrollbarState.onResize(), 10);541 this.handleThumbPositionChange = this.scrollbarState.onThumbPositionChange;542 this.handleWheelScroll = this.scrollbarState.onWheelScroll;543 this.handleThumbPointerDown = this.scrollbarState.onThumbPointerDown;544 this.handleThumbPointerUp = this.scrollbarState.onThumbPointerUp;545 $effect(() => {546 const maxScrollPos = this.maxScrollPos;547 const scrollbarNode = this.scrollbar.opts.ref.current;548 // we want to react to the viewport node changing so we leave this here549 this.root.viewportNode;550 const handleWheel = (e) => {551 const node = e.target;552 const isScrollbarWheel = scrollbarNode?.contains(node);553 if (isScrollbarWheel)554 this.handleWheelScroll(e, maxScrollPos);555 };556 const unsubListener = on(this.root.domContext.getDocument(), "wheel", handleWheel, {557 passive: false,558 });559 return unsubListener;560 });561 $effect.pre(() => {562 // react to changes to this:563 this.scrollbarVis.sizes;564 untrack(() => this.handleThumbPositionChange());565 });566 // $effect.pre(() => {567 // this.handleThumbPositionChange();568 // });569 new SvelteResizeObserver(() => this.scrollbar.opts.ref.current, this.handleResize);570 new SvelteResizeObserver(() => this.root.contentNode, this.handleResize);571 this.onpointerdown = this.onpointerdown.bind(this);572 this.onpointermove = this.onpointermove.bind(this);573 this.onpointerup = this.onpointerup.bind(this);574 this.onlostpointercapture = this.onlostpointercapture.bind(this);575 }576 handleDragScroll(e) {577 if (!this.rect)578 return;579 const x = e.clientX - this.rect.left;580 const y = e.clientY - this.rect.top;581 this.scrollbarState.onDragScroll({ x, y });582 }583 #cleanupPointerState() {584 if (this.rect === null)585 return;586 this.root.domContext.getDocument().body.style.webkitUserSelect = this.prevWebkitUserSelect;587 if (this.root.viewportNode)588 this.root.viewportNode.style.scrollBehavior = "";589 this.rect = null;590 }591 onpointerdown(e) {592 if (e.button !== 0)593 return;594 const target = e.target;595 target.setPointerCapture(e.pointerId);596 this.rect = this.scrollbar.opts.ref.current?.getBoundingClientRect() ?? null;597 // pointer capture doesn't prevent text selection in Safari598 // so we remove text selection manually when scrolling599 this.prevWebkitUserSelect = this.root.domContext.getDocument().body.style.webkitUserSelect;600 this.root.domContext.getDocument().body.style.webkitUserSelect = "none";601 if (this.root.viewportNode)602 this.root.viewportNode.style.scrollBehavior = "auto";603 this.handleDragScroll(e);604 }605 onpointermove(e) {606 this.handleDragScroll(e);607 }608 onpointerup(e) {609 const target = e.target;610 if (target.hasPointerCapture(e.pointerId)) {611 target.releasePointerCapture(e.pointerId);612 }613 this.#cleanupPointerState();614 }615 onlostpointercapture(_) {616 this.#cleanupPointerState();617 }618 props = $derived.by(() => mergeProps({619 ...this.scrollbarState.props,620 style: {621 position: "absolute",622 ...this.scrollbarState.props.style,623 },624 [scrollAreaAttrs.scrollbar]: "",625 onpointerdown: this.onpointerdown,626 onpointermove: this.onpointermove,627 onpointerup: this.onpointerup,628 onlostpointercapture: this.onlostpointercapture,629 }));630}631export class ScrollAreaThumbImplState {632 static create(opts) {633 return new ScrollAreaThumbImplState(opts, ScrollAreaScrollbarSharedContext.get());634 }635 opts;636 scrollbarState;637 attachment;638 #root;639 #removeUnlinkedScrollListener = $state();640 #debounceScrollEnd = useDebounce(() => {641 if (this.#removeUnlinkedScrollListener) {642 this.#removeUnlinkedScrollListener();643 this.#removeUnlinkedScrollListener = undefined;644 }645 }, 100);646 constructor(opts, scrollbarState) {647 this.opts = opts;648 this.scrollbarState = scrollbarState;649 this.#root = scrollbarState.root;650 this.attachment = attachRef(this.opts.ref, (v) => (this.scrollbarState.scrollbarVis.thumbNode = v));651 $effect(() => {652 const viewportNode = this.#root.viewportNode;653 if (!viewportNode)654 return;655 const handleScroll = () => {656 this.#debounceScrollEnd();657 if (!this.#removeUnlinkedScrollListener) {658 const listener = addUnlinkedScrollListener(viewportNode, this.scrollbarState.handleThumbPositionChange);659 this.#removeUnlinkedScrollListener = listener;660 this.scrollbarState.handleThumbPositionChange();661 }662 };663 untrack(() => this.scrollbarState.handleThumbPositionChange());664 const unsubListener = on(viewportNode, "scroll", handleScroll);665 return unsubListener;666 });667 this.onpointerdowncapture = this.onpointerdowncapture.bind(this);668 this.onpointerup = this.onpointerup.bind(this);669 }670 onpointerdowncapture(e) {671 const thumb = e.target;672 if (!thumb)673 return;674 const thumbRect = thumb.getBoundingClientRect();675 const x = e.clientX - thumbRect.left;676 const y = e.clientY - thumbRect.top;677 this.scrollbarState.handleThumbPointerDown({ x, y });678 }679 onpointerup(_) {680 this.scrollbarState.handleThumbPointerUp();681 }682 props = $derived.by(() => ({683 id: this.opts.id.current,684 "data-state": this.scrollbarState.scrollbarVis.hasThumb ? "visible" : "hidden",685 style: {686 width: "var(--bits-scroll-area-thumb-width)",687 height: "var(--bits-scroll-area-thumb-height)",688 transform: this.scrollbarState.scrollbarVis.prevTransformStyle,689 },690 onpointerdowncapture: this.onpointerdowncapture,691 onpointerup: this.onpointerup,692 [scrollAreaAttrs.thumb]: "",693 ...this.attachment,694 }));695}696export class ScrollAreaCornerImplState {697 static create(opts) {698 return new ScrollAreaCornerImplState(opts, ScrollAreaRootContext.get());699 }700 opts;701 root;702 attachment;703 #width = $state(0);704 #height = $state(0);705 hasSize = $derived(Boolean(this.#width && this.#height));706 constructor(opts, root) {707 this.opts = opts;708 this.root = root;709 this.attachment = attachRef(this.opts.ref);710 new SvelteResizeObserver(() => this.root.scrollbarXNode, () => {711 const height = this.root.scrollbarXNode?.offsetHeight || 0;712 this.root.cornerHeight = height;713 this.#height = height;714 });715 new SvelteResizeObserver(() => this.root.scrollbarYNode, () => {716 const width = this.root.scrollbarYNode?.offsetWidth || 0;717 this.root.cornerWidth = width;718 this.#width = width;719 });720 }721 props = $derived.by(() => ({722 id: this.opts.id.current,723 style: {724 width: this.#width,725 height: this.#height,726 position: "absolute",727 right: this.root.opts.dir.current === "ltr" ? 0 : undefined,728 left: this.root.opts.dir.current === "rtl" ? 0 : undefined,729 bottom: 0,730 },731 [scrollAreaAttrs.corner]: "",732 ...this.attachment,733 }));734}735function toInt(value) {736 return value ? Number.parseInt(value, 10) : 0;737}738function getThumbRatio(viewportSize, contentSize) {739 const ratio = viewportSize / contentSize;740 return Number.isNaN(ratio) ? 0 : ratio;741}742function getThumbSize(sizes) {743 const ratio = getThumbRatio(sizes.viewport, sizes.content);744 const scrollbarPadding = sizes.scrollbar.paddingStart + sizes.scrollbar.paddingEnd;745 const thumbSize = (sizes.scrollbar.size - scrollbarPadding) * ratio;746 return Math.max(thumbSize, 18);747}748function getScrollPositionFromPointer({ pointerPos, pointerOffset, sizes, dir = "ltr", }) {749 const thumbSizePx = getThumbSize(sizes);750 const thumbCenter = thumbSizePx / 2;751 const offset = pointerOffset || thumbCenter;752 const thumbOffsetFromEnd = thumbSizePx - offset;753 const minPointerPos = sizes.scrollbar.paddingStart + offset;754 const maxPointerPos = sizes.scrollbar.size - sizes.scrollbar.paddingEnd - thumbOffsetFromEnd;755 const maxScrollPos = sizes.content - sizes.viewport;756 const scrollRange = dir === "ltr" ? [0, maxScrollPos] : [maxScrollPos * -1, 0];757 const interpolate = linearScale([minPointerPos, maxPointerPos], scrollRange);758 return interpolate(pointerPos);759}760function getThumbOffsetFromScroll({ scrollPos, sizes, dir = "ltr", }) {761 const thumbSizePx = getThumbSize(sizes);762 const scrollbarPadding = sizes.scrollbar.paddingStart + sizes.scrollbar.paddingEnd;763 const scrollbar = sizes.scrollbar.size - scrollbarPadding;764 const maxScrollPos = sizes.content - sizes.viewport;765 const maxThumbPos = scrollbar - thumbSizePx;766 const scrollClampRange = dir === "ltr" ? [0, maxScrollPos] : [maxScrollPos * -1, 0];767 const scrollWithoutMomentum = clamp(scrollPos, scrollClampRange[0], scrollClampRange[1]);768 const interpolate = linearScale([0, maxScrollPos], [0, maxThumbPos]);769 return interpolate(scrollWithoutMomentum);770}771// https://github.com/tmcw-up-for-adoption/simple-linear-scale/blob/master/index.js772function linearScale(input, output) {773 return (value) => {774 if (input[0] === input[1] || output[0] === output[1])775 return output[0];776 const ratio = (output[1] - output[0]) / (input[1] - input[0]);777 return output[0] + ratio * (value - input[0]);778 };779}780function isScrollingWithinScrollbarBounds(scrollPos, maxScrollPos) {781 return scrollPos > 0 && scrollPos < maxScrollPos;782}783function addUnlinkedScrollListener(node, handler) {784 let prevPosition = { left: node.scrollLeft, top: node.scrollTop };785 let rAF = 0;786 const win = getWindow(node);787 (function loop() {788 const position = { left: node.scrollLeft, top: node.scrollTop };789 const isHorizontalScroll = prevPosition.left !== position.left;790 const isVerticalScroll = prevPosition.top !== position.top;791 if (isHorizontalScroll || isVerticalScroll)792 handler();793 prevPosition = position;794 rAF = win.requestAnimationFrame(loop);795 })();796 return () => win.cancelAnimationFrame(rAF);797}798 