coomdev
2 years ago
21 changed files with 3218 additions and 2126 deletions
File diff suppressed because it is too large
@ -1,255 +0,0 @@ |
|||
<script lang="ts"> |
|||
import { hasContext, onDestroy } from 'svelte' |
|||
import Dialog from './Dialog.svelte'; |
|||
|
|||
import { settings } from './stores' |
|||
import Tag from './Tag.svelte' |
|||
import type { Booru } from './thirdeye'; |
|||
|
|||
let newbooru: Partial<Omit<Booru, 'quirks'> & {view: string}> = {}; |
|||
let dial: Dialog; |
|||
|
|||
function appendBooru() { |
|||
$settings.rsources = [...$settings.rsources, newbooru as any]; |
|||
dial.toggle(); |
|||
newbooru = {} |
|||
} |
|||
|
|||
let visible = false |
|||
let penisEvent = () => { |
|||
console.log('bepis') |
|||
visible = !visible |
|||
} |
|||
document.addEventListener('penis', penisEvent) |
|||
console.log('app loaded') |
|||
|
|||
function removeTag(t: string) { |
|||
$settings.blacklist = $settings.blacklist.filter((e: any) => e != t) |
|||
} |
|||
|
|||
function removeBooru(t: string) { |
|||
const idx = $settings.rsources.findIndex(e => e.domain == t) |
|||
const rep = prompt("You DO know what you're doing, right? (type 'y')") |
|||
if (!rep || rep != 'y') return |
|||
if (idx >= 0) $settings.rsources.splice(idx, 1) |
|||
$settings.rsources = $settings.rsources |
|||
} |
|||
|
|||
function toggleBooru(t: string) { |
|||
const elem = $settings.rsources.find(e => e.domain == t) |
|||
if (elem) |
|||
elem.disabled = !elem.disabled; |
|||
$settings.rsources = $settings.rsources |
|||
} |
|||
|
|||
onDestroy(() => { |
|||
document.removeEventListener('penis', penisEvent) |
|||
}) |
|||
</script> |
|||
|
|||
<div class="backpanel" class:enabled={visible} class:disabled={!visible}> |
|||
<div class="content"> |
|||
<h1>PEE Settings</h1> |
|||
<hr /> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.vercheck} /> |
|||
Check for new versions at startup. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.xpi} /> |
|||
Autoexpand Images on opening. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.xpv} /> |
|||
Autoexpand Videos on opening. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.loop} /> |
|||
Loop media content. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.dh} /> |
|||
Disable hover preview. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.eye} /> |
|||
Hide embedded content behind an eye. |
|||
</label> |
|||
{#if $settings.eye} |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.ho} /> |
|||
Hide original content when hidden content is visible. |
|||
</label> |
|||
{/if} |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.pre} /> |
|||
Preload external files. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.prev} /> |
|||
Preload external files when they are in view. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.hotlink} /> |
|||
Hotlink content. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.ca} /> |
|||
Control audio on videos with mouse wheel. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.sh} /> |
|||
Show Minimap |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.ep} /> |
|||
<!-- svelte-ignore a11y-missing-attribute --> |
|||
Disable embedded file preloading<a |
|||
title="You might still want to enable 'preload external files'">?</a |
|||
> |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.te} /> |
|||
Disable third-eye. |
|||
</label> |
|||
{#if !$settings.te} |
|||
<h3>Booru sources</h3> |
|||
<div class="tagcont"> |
|||
{#each $settings.rsources as source, i} |
|||
<Tag |
|||
tag={source.name} |
|||
on:remove={() => removeBooru(source.domain)} |
|||
on:toggle={() => toggleBooru(source.domain)} |
|||
toggleable={true} |
|||
toggled={!$settings.rsources.find(e => e.domain == source.domain) |
|||
?.disabled} |
|||
/> |
|||
{/each} |
|||
</div> |
|||
<button |
|||
on:click={ev => { |
|||
dial.setPos([ev.clientX, ev.clientY]) |
|||
dial.toggle() |
|||
}}>Add a source</button |
|||
> |
|||
<Dialog bind:this={dial}> |
|||
<div class="form"> |
|||
<label> |
|||
Name |
|||
<input |
|||
type="text" |
|||
placeholder="Gelbooru" |
|||
bind:value={newbooru.name} |
|||
/> |
|||
</label> |
|||
<label> |
|||
Domain |
|||
<input |
|||
type="text" |
|||
placeholder="gelbooru.com" |
|||
bind:value={newbooru.domain} |
|||
/> |
|||
</label> |
|||
<label> |
|||
API Endpoint |
|||
<input |
|||
type="text" |
|||
placeholder="/post.json?tags=md5:" |
|||
bind:value={newbooru.endpoint} |
|||
/> |
|||
</label> |
|||
<label> |
|||
Post page prefix (for sources) |
|||
<input |
|||
type="text" |
|||
placeholder="https://yande.re/post/show/" |
|||
bind:value={newbooru.view} |
|||
/> |
|||
</label> |
|||
<button on:click={appendBooru}>Add</button> |
|||
</div> |
|||
</Dialog> |
|||
|
|||
<hr /> |
|||
<h3>Blacklisted tags</h3> |
|||
<div class="tagcont"> |
|||
{#each $settings.blacklist as tag, i} |
|||
<Tag {tag} on:toggle={() => removeTag(tag)} /> |
|||
{/each} |
|||
</div> |
|||
<input |
|||
placeholder="Press enter after typing your tag" |
|||
on:keydown={ev => { |
|||
if (ev.key == 'Enter') { |
|||
$settings.blacklist = [ |
|||
...$settings.blacklist, |
|||
ev.currentTarget.value, |
|||
] |
|||
ev.currentTarget.value = '' |
|||
} |
|||
}} |
|||
/> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
<style scoped> |
|||
.tagcont { |
|||
display: flex; |
|||
gap: 5px; |
|||
margin-bottom: 10px; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.enabled { |
|||
display: block; |
|||
} |
|||
|
|||
.disabled { |
|||
display: none; |
|||
} |
|||
|
|||
.content { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
hr { |
|||
width: 100%; |
|||
} |
|||
|
|||
h1 { |
|||
text-align: center; |
|||
} |
|||
.form { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20px; |
|||
position: absolute; |
|||
padding: 15px; |
|||
border: 1px solid white; |
|||
background-color: inherit; |
|||
border-radius: 10px; |
|||
} |
|||
|
|||
.form > label { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10px; |
|||
} |
|||
|
|||
.backpanel { |
|||
position: absolute; |
|||
right: 32px; |
|||
padding: 10px; |
|||
width: 15%; |
|||
top: 32px; |
|||
border: 1px solid; |
|||
border-radius: 5px; |
|||
background-color: rgba(0, 0, 0, 0.2); |
|||
pointer-events: all; |
|||
backdrop-filter: blur(9px); |
|||
max-height: 80vh; |
|||
min-width: 321px; |
|||
} |
|||
</style> |
@ -0,0 +1,286 @@ |
|||
<script lang="ts"> |
|||
import { hasContext, onDestroy } from 'svelte' |
|||
import Dialog from './Dialog.svelte' |
|||
|
|||
|
|||
import Tag from './Tag.svelte' |
|||
import type { Booru } from '../thirdeye' |
|||
import Tabs from './Tabs.svelte' |
|||
import TabList from './TabList.svelte' |
|||
import Tab from './Tab.svelte' |
|||
import TabPanel from './TabPanel.svelte' |
|||
|
|||
import { settings } from '../stores' |
|||
import { filehosts } from '../filehosts' |
|||
|
|||
let newbooru: Partial<Omit<Booru, 'quirks'> & { view: string }> = {} |
|||
let dial: Dialog |
|||
|
|||
function appendBooru() { |
|||
$settings.rsources = [...$settings.rsources, newbooru as any] |
|||
dial.toggle() |
|||
newbooru = {} |
|||
} |
|||
|
|||
let visible = false |
|||
let penisEvent = () => { |
|||
console.log('bepis') |
|||
visible = !visible |
|||
} |
|||
document.addEventListener('penis', penisEvent) |
|||
console.log('app loaded') |
|||
|
|||
function removeTag(t: string) { |
|||
$settings.blacklist = $settings.blacklist.filter((e: any) => e != t) |
|||
} |
|||
|
|||
function removeBooru(t: string) { |
|||
const idx = $settings.rsources.findIndex((e) => e.domain == t) |
|||
const rep = prompt("You DO know what you're doing, right? (type 'y')") |
|||
if (!rep || rep != 'y') return |
|||
if (idx >= 0) $settings.rsources.splice(idx, 1) |
|||
$settings.rsources = $settings.rsources |
|||
} |
|||
|
|||
function toggleBooru(t: string) { |
|||
const elem = $settings.rsources.find((e) => e.domain == t) |
|||
if (elem) elem.disabled = !elem.disabled |
|||
$settings.rsources = $settings.rsources |
|||
} |
|||
|
|||
onDestroy(() => { |
|||
document.removeEventListener('penis', penisEvent) |
|||
}) |
|||
</script> |
|||
|
|||
<div class="backpanel" class:enabled={visible} class:disabled={!visible}> |
|||
<div class="content"> |
|||
<h1>PEE Settings</h1> |
|||
<hr /> |
|||
<Tabs> |
|||
<TabList> |
|||
<Tab>General</Tab> |
|||
<Tab>External</Tab> |
|||
<Tab>File Host</Tab> |
|||
</TabList> |
|||
<TabPanel> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.vercheck} /> |
|||
Check for new versions at startup. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.xpi} /> |
|||
Autoexpand Images on opening. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.xpv} /> |
|||
Autoexpand Videos on opening. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.loop} /> |
|||
Loop media content. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.dh} /> |
|||
Disable hover preview. |
|||
</label> |
|||
|
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.eye} /> |
|||
Hide embedded content behind an eye. |
|||
</label> |
|||
{#if $settings.eye} |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.ho} /> |
|||
Hide original content when hidden content is visible. |
|||
</label> |
|||
{/if} |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.pre} /> |
|||
Preload external files. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.prev} /> |
|||
Preload external files when they are in view. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.hotlink} /> |
|||
Hotlink content. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.ca} /> |
|||
Control audio on videos with mouse wheel. |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.sh} /> |
|||
Show Minimap |
|||
</label> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.ep} /> |
|||
<!-- svelte-ignore a11y-missing-attribute --> |
|||
Disable embedded file preloading<a |
|||
title="You might still want to enable 'preload external files'">?</a |
|||
> |
|||
</label> |
|||
</TabPanel> |
|||
<TabPanel> |
|||
<label> |
|||
<input type="checkbox" bind:checked={$settings.te} /> |
|||
Disable third-eye. |
|||
</label> |
|||
{#if !$settings.te} |
|||
<h3>Booru sources</h3> |
|||
<div class="tagcont"> |
|||
{#each $settings.rsources as source, i} |
|||
<Tag |
|||
tag={source.name} |
|||
on:remove={() => removeBooru(source.domain)} |
|||
on:toggle={() => toggleBooru(source.domain)} |
|||
toggleable={true} |
|||
toggled={!$settings.rsources.find( |
|||
(e) => e.domain == source.domain, |
|||
)?.disabled} |
|||
/> |
|||
{/each} |
|||
</div> |
|||
<button |
|||
on:click={(ev) => { |
|||
dial.setPos([ev.clientX, ev.clientY]) |
|||
dial.toggle() |
|||
}}>Add a source</button |
|||
> |
|||
<Dialog bind:this={dial}> |
|||
<div class="form"> |
|||
<label> |
|||
Name |
|||
<input |
|||
type="text" |
|||
placeholder="Gelbooru" |
|||
bind:value={newbooru.name} |
|||
/> |
|||
</label> |
|||
<label> |
|||
Domain |
|||
<input |
|||
type="text" |
|||
placeholder="gelbooru.com" |
|||
bind:value={newbooru.domain} |
|||
/> |
|||
</label> |
|||
<label> |
|||
API Endpoint |
|||
<input |
|||
type="text" |
|||
placeholder="/post.json?tags=md5:" |
|||
bind:value={newbooru.endpoint} |
|||
/> |
|||
</label> |
|||
<label> |
|||
Post page prefix (for sources) |
|||
<input |
|||
type="text" |
|||
placeholder="https://yande.re/post/show/" |
|||
bind:value={newbooru.view} |
|||
/> |
|||
</label> |
|||
<button on:click={appendBooru}>Add</button> |
|||
</div> |
|||
</Dialog> |
|||
|
|||
<hr /> |
|||
<h3>Blacklisted tags</h3> |
|||
<div class="tagcont"> |
|||
{#each $settings.blacklist as tag, i} |
|||
<Tag {tag} on:toggle={() => removeTag(tag)} /> |
|||
{/each} |
|||
</div> |
|||
<input |
|||
placeholder="Press enter after typing your tag" |
|||
on:keydown={(ev) => { |
|||
if (ev.key == 'Enter') { |
|||
$settings.blacklist = [ |
|||
...$settings.blacklist, |
|||
ev.currentTarget.value, |
|||
] |
|||
ev.currentTarget.value = '' |
|||
} |
|||
}} |
|||
/> |
|||
{/if} |
|||
</TabPanel> |
|||
<TabPanel> |
|||
<p>Host to use when uploading files (Only permanent hosts)</p> |
|||
<select bind:value={$settings.fhost}> |
|||
{#each filehosts as fh, i} |
|||
<option value={i}>{fh.domain}</option> |
|||
{/each} |
|||
</select> |
|||
<label> |
|||
Maximum number of embedded links to display |
|||
<input type="number" bind:value={$settings.maxe} /> |
|||
</label> |
|||
</TabPanel> |
|||
</Tabs> |
|||
</div> |
|||
</div> |
|||
|
|||
<style scoped> |
|||
.tagcont { |
|||
display: flex; |
|||
gap: 5px; |
|||
margin-bottom: 10px; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.enabled { |
|||
display: block; |
|||
} |
|||
|
|||
.disabled { |
|||
display: none; |
|||
} |
|||
|
|||
.content { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
hr { |
|||
width: 100%; |
|||
} |
|||
|
|||
h1 { |
|||
text-align: center; |
|||
} |
|||
.form { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20px; |
|||
position: absolute; |
|||
padding: 15px; |
|||
border: 1px solid white; |
|||
background-color: inherit; |
|||
border-radius: 10px; |
|||
} |
|||
|
|||
.form > label { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10px; |
|||
} |
|||
|
|||
.backpanel { |
|||
position: absolute; |
|||
right: 32px; |
|||
padding: 10px; |
|||
width: 15%; |
|||
top: 32px; |
|||
border: 1px solid; |
|||
border-radius: 5px; |
|||
background-color: rgba(0, 0, 0, 0.2); |
|||
pointer-events: all; |
|||
backdrop-filter: blur(9px); |
|||
max-height: 80vh; |
|||
min-width: 321px; |
|||
} |
|||
</style> |
@ -1,5 +1,5 @@ |
|||
<script lang="ts"> |
|||
import type { EmbeddedFile } from './main'; |
|||
import type { EmbeddedFile } from '../main'; |
|||
import { createEventDispatcher } from 'svelte'; |
|||
import Embedding from './Embedding.svelte'; |
|||
|
@ -1,5 +1,5 @@ |
|||
<script lang="ts"> |
|||
import type { fireNotification } from './utils' |
|||
import type { fireNotification } from '../utils' |
|||
|
|||
type t = Parameters<typeof fireNotification> |
|||
type Notification = { |
@ -1,9 +1,6 @@ |
|||
<script lang="ts"> |
|||
import { fileTypeFromBuffer } from 'file-type'; |
|||
import { onDestroy, onMount } from 'svelte'; |
|||
import type { EmbeddedFile } from './main'; |
|||
|
|||
import { settings, appState } from './stores' |
|||
import { settings, appState } from '../stores' |
|||
|
|||
function getOffset(el: HTMLElement | null) { |
|||
var _x = 0; |
@ -0,0 +1,34 @@ |
|||
<script lang="ts"> |
|||
import { getContext } from 'svelte' |
|||
import { TABS } from './Tabs.svelte' |
|||
|
|||
const tab = {} |
|||
const { registerTab, selectTab, selectedTab } = getContext(TABS) |
|||
|
|||
registerTab(tab) |
|||
</script> |
|||
|
|||
<button class:selected={$selectedTab === tab} on:click={() => selectTab(tab)}> |
|||
<slot /> |
|||
</button> |
|||
|
|||
<style> |
|||
button { |
|||
background: none; |
|||
border: none; |
|||
border-bottom: 2px solid white; |
|||
border-radius: 0; |
|||
margin: 0; |
|||
color: unset; |
|||
} |
|||
|
|||
button:hover { |
|||
cursor: pointer; |
|||
background-color: #8d8d8d80; |
|||
} |
|||
|
|||
.selected { |
|||
border-bottom: 2px solid; |
|||
color: #333; |
|||
} |
|||
</style> |
@ -0,0 +1,9 @@ |
|||
<div class="tab-list"> |
|||
<slot /> |
|||
</div> |
|||
|
|||
<style> |
|||
.tab-list { |
|||
border-bottom: 1px solid; |
|||
} |
|||
</style> |
@ -0,0 +1,12 @@ |
|||
<script lang="ts"> |
|||
import { getContext } from 'svelte' |
|||
import { TABS } from './Tabs.svelte' |
|||
const panel = {} |
|||
const { registerPanel, selectedPanel } = getContext(TABS) |
|||
|
|||
registerPanel(panel) |
|||
</script> |
|||
|
|||
{#if $selectedPanel === panel} |
|||
<slot /> |
|||
{/if} |
@ -0,0 +1,63 @@ |
|||
<script context="module"> |
|||
export const TABS = {} |
|||
</script> |
|||
|
|||
<script lang="ts"> |
|||
import { setContext, onDestroy } from 'svelte' |
|||
import { writable } from 'svelte/store' |
|||
import type Tab from './Tab.svelte' |
|||
import type TabPanel from './TabPanel.svelte' |
|||
|
|||
const tabs: Tab[] = [] |
|||
const panels: TabPanel[] = [] |
|||
const selectedTab = writable<Tab | null>(null) |
|||
const selectedPanel = writable<TabPanel | null>(null) |
|||
|
|||
setContext(TABS, { |
|||
registerTab: (tab: Tab) => { |
|||
tabs.push(tab) |
|||
selectedTab.update((current) => current || tab) |
|||
|
|||
onDestroy(() => { |
|||
const i = tabs.indexOf(tab) |
|||
tabs.splice(i, 1) |
|||
selectedTab.update((current) => |
|||
current === tab ? tabs[i] || tabs[tabs.length - 1] : current, |
|||
) |
|||
}) |
|||
}, |
|||
|
|||
registerPanel: (panel: TabPanel) => { |
|||
panels.push(panel) |
|||
selectedPanel.update((current) => current || panel) |
|||
|
|||
onDestroy(() => { |
|||
const i = panels.indexOf(panel) |
|||
panels.splice(i, 1) |
|||
selectedPanel.update((current) => |
|||
current === panel ? panels[i] || panels[panels.length - 1] : current, |
|||
) |
|||
}) |
|||
}, |
|||
|
|||
selectTab: (tab: Tab) => { |
|||
const i = tabs.indexOf(tab) |
|||
selectedTab.set(tab) |
|||
selectedPanel.set(panels[i]) |
|||
}, |
|||
|
|||
selectedTab, |
|||
selectedPanel, |
|||
}) |
|||
</script> |
|||
|
|||
<div class="tabs"> |
|||
<slot /> |
|||
</div> |
|||
|
|||
<style scoped> |
|||
.tabs { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
</style> |
@ -0,0 +1,47 @@ |
|||
import { GM_fetch } from "./requests"; |
|||
|
|||
const lolisafe = (domain: string) => ({ |
|||
domain, |
|||
async uploadFile(f: Blob) { |
|||
return ''; |
|||
} |
|||
}); |
|||
|
|||
function parseForm(data: object) { |
|||
const form = new FormData(); |
|||
|
|||
Object.entries(data) |
|||
.filter(([key, value]) => value !== null) |
|||
.map(([key, value]) => form.append(key, value)); |
|||
|
|||
return form; |
|||
} |
|||
|
|||
const catbox = (domain: string) => ({ |
|||
domain, |
|||
async uploadFile(inj: Blob) { |
|||
const resp = await GM_fetch(`https://${domain}/user/api.php`, { |
|||
method: 'POST', |
|||
body: parseForm({ |
|||
reqtype: 'fileupload', |
|||
fileToUpload: inj |
|||
}) |
|||
}); |
|||
return resp.text(); |
|||
} |
|||
}); |
|||
|
|||
export type API = { |
|||
domain: string; |
|||
uploadFile(f: Blob): Promise<string>; |
|||
} |
|||
|
|||
export const filehosts: API[] = [ |
|||
catbox('catbox.moe'), |
|||
lolisafe('zz.ht'), |
|||
lolisafe('imouto.kawaii.su'), |
|||
lolisafe('take-me-to.space'), |
|||
lolisafe('loli.solutions'), |
|||
lolisafe('loli.graphics'), |
|||
lolisafe('sucks-to-b.eu') |
|||
]; |
Loading…
Reference in new issue