Can embed any file in a PNG/WebM/GIF/JPEG and upload it to a third-party host through 4chan
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

127 lines
3.4 KiB

<script lang="ts">
import { fileTypeFromBuffer } from 'file-type';
import { onDestroy, onMount } from 'svelte';
import type { EmbeddedFile } from './main';
import { settings, appState } from './stores'
function getOffset(el: HTMLElement | null) {
var _x = 0;
var _y = 0;
while(el && el instanceof HTMLElement) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent as HTMLElement;
}
return { top: _y, left: _x };
}
let positions:[number, number, number, string][] = [];
const getViewport = () => (typeof visualViewport != "undefined" ? () => [visualViewport.width, visualViewport.height] : () => [document.documentElement.clientWidth, document.documentElement.clientHeight])();
const getDistFromTop = () => (typeof visualViewport != "undefined" ? () => visualViewport.pageTop : () => document.documentElement.scrollTop)();
let viewhint: HTMLSpanElement;
const updatePositions = (v: typeof $appState) => {
const [sw, sh] = getViewport();
const containerScrollHeight = document.documentElement.scrollHeight;
positions = v.foundPosts.map(v => {
const coords = getOffset(v);
const top = sh * (coords.top / containerScrollHeight);
const bot = sh * ((coords.top + v.offsetHeight) / containerScrollHeight);
const hei = (bot - top);
return [top, hei, coords.top, getComputedStyle(v)['borderRightColor']];
})
}
const updateViewhint = () => {
if (!$settings.sh) return;
const [sw, sh] = getViewport();
const fromtop = getDistFromTop();
const containerScrollHeight = document.documentElement.scrollHeight;
const top = sh * (fromtop / containerScrollHeight);
const bot = sh * ((fromtop + sh) / containerScrollHeight);
const hei = (bot - top);
viewhint.style.top = top + 'px';
viewhint.style.height = hei + 'px';
}
appState.subscribe(v => updatePositions(v));
const handleResize = () => {
updatePositions($appState);
};
let locked = false;
const handleScroll = async () => {
if (locked) return;
locked = true;
updateViewhint();
await new Promise(_ => requestAnimationFrame(_));
locked = false;
};
const docRszObserver = new ResizeObserver(e => {
updatePositions($appState)
updateViewhint();
});
onMount(() => {
window.addEventListener('resize', handleResize);
document.addEventListener('scroll', handleScroll);
updateViewhint();
docRszObserver.observe(document.documentElement);
});
onDestroy(() => {
window.removeEventListener('resize', handleResize);
document.addEventListener('scroll', handleScroll);
docRszObserver.unobserve(document.documentElement);
})
</script>
{#if $settings.sh}
<div class="scroll-container">
{#each $appState.foundPosts as post, i}
<span
on:click={() => window.scrollTo(0, positions[i][2])}
style="top: {positions[i][0]}px; height: {positions[
i
][1]}px; background-color: {positions[i][3]}"
class="marker"
/>
{/each}
<span class="hint" bind:this={viewhint} />
</div>
{/if}
<style scoped>
.hint {
background-color: rgb(222 222 222 / 80%);
z-index: -1;
pointer-events: none;
}
.scroll-container {
position: fixed;
height: 100%;
width: 12px;
/* pointer-events: none; */
top: 0;
right: 0;
z-index: 1000;
}
.scroll-container span {
/* markers */
position: absolute;
right: 0;
width: 33%;
cursor: pointer;
transition: width 200ms;
}
.scroll-container:hover span {
width: 100%;
}
</style>