[feat] added license

This commit is contained in:
kwaroran
2023-07-19 22:01:23 +09:00
parent dfc27df7c7
commit 07f6117a01
13 changed files with 269 additions and 106 deletions

View File

@@ -46,6 +46,7 @@
"rollup": "^3.21.3",
"showdown": "^2.1.0",
"three": "^0.154.0",
"tippy.js": "^6.3.7",
"uuid": "^9.0.0",
"wasmoon": "^1.15.0",
"web-streams-polyfill": "^3.2.1"

13
pnpm-lock.yaml generated
View File

@@ -101,6 +101,9 @@ dependencies:
three:
specifier: ^0.154.0
version: 0.154.0
tippy.js:
specifier: ^6.3.7
version: 6.3.7
uuid:
specifier: ^9.0.0
version: 9.0.0
@@ -491,6 +494,10 @@ packages:
fastq: 1.15.0
dev: true
/@popperjs/core@2.11.8:
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
dev: false
/@rollup/plugin-virtual@3.0.1(rollup@3.21.3):
resolution: {integrity: sha512-fK8O0IL5+q+GrsMLuACVNk2x21g3yaw+sG2qn16SnUd3IlBsQyvWxLMGHmCmXRMecPjGRSZ/1LmZB4rjQm68og==}
engines: {node: '>=14.0.0'}
@@ -2853,6 +2860,12 @@ packages:
resolution: {integrity: sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==}
dev: false
/tippy.js@6.3.7:
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
dependencies:
'@popperjs/core': 2.11.8
dev: false
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}

View File

@@ -8,6 +8,8 @@
import { User } from 'lucide-svelte';
import { hubURL } from 'src/ts/characterCards';
import TextInput from '../UI/GUI/TextInput.svelte';
import { openURL } from 'src/ts/storage/globalApi';
import Button from '../UI/GUI/Button.svelte';
let btn
let input = ''
@@ -56,49 +58,70 @@
{@html msg}
{/await}
</span>
{:else if $alertStore.type === 'tos'}
<!-- svelte-ignore a11y-missing-attribute -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="text-white">You should accept RisuRealm's <a class="text-green-600 hover:text-green-500 transition-colors duration-200 cursor-pointer" on:click={() => {
openURL('https://sv.risuai.xyz/hub/tos')
}}>Terms of Service</a> to continue</div>
{:else if $alertStore.type !== 'select'}
<span class="text-gray-300">{$alertStore.msg}</span>
{/if}
{#if $alertStore.type === 'ask'}
<div class="flex gap-2 w-full">
<button bind:this={btn} class="mt-4 border-borderc bg-transparent outline-none border-solid border-1 p-2 text-lg text-neutral-200 hover:bg-green-500 transition-colors flex-1 focus:border-3" on:click={() => {
<Button className="mt-4 flex-grow" on:click={() => {
alertStore.set({
type: 'none',
msg: 'yes'
})
}}>YES</button>
<button class="mt-4 border-borderc bg-transparent outline-none border-solid border-1 p-2 text-lg text-neutral-200 hover:bg-red-500 transition-colors focus:border-3 flex-1" on:click={() => {
}}>YES</Button>
<Button className="mt-4 flex-grow" on:click={() => {
alertStore.set({
type: 'none',
msg: 'no'
})
}}>NO</button>
}}>NO</Button>
</div>
{:else if $alertStore.type === 'tos'}
<div class="flex gap-2 w-full">
<Button className="mt-4 flex-grow" on:click={() => {
alertStore.set({
type: 'none',
msg: 'yes'
})
}}>Accept</Button>
<Button className="mt-4 flex-grow" on:click={() => {
alertStore.set({
type: 'none',
msg: 'no'
})
}}>Do not Accept</Button>
</div>
{:else if $alertStore.type === 'select'}
{#each $alertStore.msg.split('||') as n, i}
<button bind:this={btn} class="mt-4 border-borderc bg-transparent outline-none border-solid border-1 p-2 text-lg text-neutral-200 hover:bg-green-500 transition-colors focus:border-3" on:click={() => {
<Button className="mt-4" on:click={() => {
alertStore.set({
type: 'none',
msg: i.toString()
})
}}>{n}</button>
}}>{n}</Button>
{/each}
{:else if $alertStore.type === 'error' || $alertStore.type === 'normal' || $alertStore.type === 'markdown'}
<button bind:this={btn} class="mt-4 border-borderc bg-transparent outline-none border-solid border-1 p-2 text-lg text-neutral-200 hover:bg-green-500 transition-colors focus:border-3" on:click={() => {
<Button bind:this={btn} className="mt-4" on:click={() => {
alertStore.set({
type: 'none',
msg: ''
})
}}>OK</button>
}}>OK</Button>
{:else if $alertStore.type === 'input'}
<TextInput value="" id="alert-input" autocomplete="off"/>
<button bind:this={btn} class="mt-4 border-borderc bg-transparent outline-none border-solid border-1 p-2 text-lg text-neutral-200 hover:bg-green-500 transition-colors focus:border-3" on:click={() => {
<TextInput value="" id="alert-input" autocomplete="off" marginTop />
<Button className="mt-4" on:click={() => {
alertStore.set({
type: 'none',
//@ts-ignore
msg: document.querySelector('#alert-input')?.value
})
}}>OK</button>
}}>OK</Button>
{:else if $alertStore.type === 'login'}
<div class="fixed top-0 left-0 bg-black bg-opacity-50 w-full h-full flex justify-center items-center">
<iframe src={hubURL + '/hub/login'} title="login" class="w-full h-full">

View File

@@ -37,7 +37,7 @@
import { v4 } from "uuid";
import { checkCharOrder } from "src/ts/storage/globalApi";
import { doingChat } from "src/ts/process";
import { BotCreator } from "src/ts/creator/creator";
import { BotCreator } from "src/ts/copyright/creator";
import Button from "../UI/GUI/Button.svelte";
let openPresetList = false;
let sideBarMode = 0;

View File

@@ -7,6 +7,7 @@
class:px-4={padding}
class:py-2={padding}
class:mb-4={marginBottom}
class:mt-4={marginTop}
class:w-full={fullwidth}
class:h-full={fullh}
{autocomplete}
@@ -26,6 +27,7 @@
export let id:string = undefined
export let padding = true
export let marginBottom = false
export let marginTop = false
export let onInput = () => {}
export let fullwidth = false
export let fullh = false

View File

@@ -0,0 +1,30 @@
<script lang="ts">
import { CCLicenseData, tooltip } from "src/ts/copyright/license";
import { openURL } from "src/ts/storage/globalApi";
export let license = ""
</script>
{#if Object.keys(CCLicenseData).includes(license)}
<div class="w-full flex flex-row">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="flex flex-wrap flex-row gap-1 mt-2 items-center cursor-pointer" use:tooltip={CCLicenseData[license][1]} on:click|stopPropagation={() => {
openURL(`https://creativecommons.org/licenses/${CCLicenseData[license][0]}/4.0/`)
}}>
<img alt="creative commons" class="cc" src="https://i.creativecommons.org/l/{CCLicenseData[license][0]}/4.0/88x31.png" />
<span class="text-gray-500">
Licensed with {license}
</span>
</div>
</div>
{/if}
<style>
.cc{
width: 88px;
height: 31px;
border-width: 0;
}
</style>

View File

@@ -7,6 +7,7 @@
import RisuHubIcon from "./RealmHubIcon.svelte";
import { DataBase } from "src/ts/storage/database";
import TextInput from "../GUI/TextInput.svelte";
import RealmPopUp from "./RealmPopUp.svelte";
let openedData:null|hubType = null
@@ -104,93 +105,11 @@
</div>
</div>
{#if openedData}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="top-0 left-0 z-50 fixed w-full h-full bg-black bg-opacity-50 flex justify-center items-center" on:click={() => {
openedData = null
}}>
<div class="p-6 max-w-full bg-darkbg rounded-md flex flex-col gap-4 w-2xl overflow-y-auto">
<div class="w-full flex flex-col">
<h1 class="text-2xl font-bold max-w-full overflow-hidden whitespace-nowrap text-ellipsis">{openedData.name}</h1>
<div class="flex justify-start gap-4 mt-4">
<img class="h-36 w-36 rounded-md object-top object-cover" alt={openedData.name} src={`${hubURL}/resource/` + openedData.img}>
<span class="text-gray-400 break-words text-base chattext prose prose-invert">
{#await parseMarkdownSafe(openedData.desc) then msg}
{@html msg}
{/await}
</span>
</div>
<div class="flex justify-start gap-2 mt-4">
{#each openedData.tags as tag, i}
<div class="text-xs p-1 text-blue-400">{tag}</div>
{/each}
</div>
<div class="flex flex-wrap w-full flex-row gap-1 mt-2">
<span class="text-gray-500">
{language.chatAssumed.replace('{}', openedData.download.toString())}
</span>
<div class="border-l-selected border-l ml-1 mr-1"></div>
{#if openedData.viewScreen === 'emotion'}
<button class="text-gray-500 hover:text-green-500 transition-colors" on:click|stopPropagation={() => {alertNormal("This character includes emotion images")}}><SmileIcon /></button>
{/if}
{#if openedData.hasLore}
<button class="text-gray-500 hover:text-green-500 transition-colors" on:click|stopPropagation={() => {alertNormal("This character includes lorebook")}}><BookIcon /></button>
{/if}
</div>
</div>
<div class="flex flex-row-reverse gap-2">
<button class="text-gray-400 hover:text-red-500" on:click|stopPropagation={async () => {
const conf = await alertConfirm('Report this character?')
if(conf){
const report = await alertInput('Write a report text that would be sent to the admin')
const da = await fetch(hubURL + '/hub/report', {
method: "POST",
body: JSON.stringify({
id: openedData.id,
report: report
})
})
alertNormal(await da.text())
}
}}>
<FlagIcon />
</button>
{#if ($DataBase.account?.token?.split('-') ?? [])[1] === openedData.creator}
<button class="text-gray-400 hover:text-red-500" on:click|stopPropagation={async () => {
const conf = await alertConfirm('Do you want to remove this character from Realm?')
if(conf){
const da = await fetch(hubURL + '/hub/remove', {
method: "POST",
body: JSON.stringify({
id: openedData.id,
token: $DataBase.account?.token
})
})
alertNormal(await da.text())
}
}}>
<TrashIcon />
</button>
{/if}
<button class="text-gray-400 hover:text-green-500" on:click|stopPropagation={async () => {
await navigator.clipboard.writeText(`https://risuai.xyz/?realm=${openedData.id}`)
alertNormal("Copied to clipboard")
}}>
<PaperclipIcon />
</button>
<button class="bg-selected hover:ring flex-grow p-2 font-bold rounded-md mr-2" on:click={() => {
downloadRisuHub(openedData.id)
openedData = null
}}>
Download
</button>
</div>
</div>
</div>
<RealmPopUp bind:openedData={openedData} />
{/if}
{#if menuOpen}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="top-0 left-0 z-50 fixed w-full h-full bg-black bg-opacity-50 flex justify-center items-center" on:click={() => {

View File

@@ -0,0 +1,98 @@
<script lang="ts">
import { BookIcon, FlagIcon, PaperclipIcon, SmileIcon, TrashIcon } from "lucide-svelte";
import { language } from "src/lang";
import { alertConfirm, alertInput, alertNormal } from "src/ts/alert";
import { hubURL, type hubType, downloadRisuHub } from "src/ts/characterCards";
import { parseMarkdownSafe } from "src/ts/parser";
import { DataBase } from "src/ts/storage/database";
import RealmLicense from "./RealmLicense.svelte";
export let openedData:hubType
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="top-0 left-0 z-50 fixed w-full h-full bg-black bg-opacity-50 flex justify-center items-center" on:click={() => {
openedData = null
}}>
<div class="p-6 max-w-full bg-darkbg rounded-md flex flex-col gap-4 w-2xl overflow-y-auto">
<div class="w-full flex flex-col">
<h1 class="text-2xl font-bold max-w-full overflow-hidden whitespace-nowrap text-ellipsis">{openedData.name}</h1>
<div class="flex justify-start gap-4 mt-4">
<img class="h-36 w-36 rounded-md object-top object-cover" alt={openedData.name} src={`${hubURL}/resource/` + openedData.img}>
<span class="text-gray-400 break-words text-base chattext prose prose-invert">
{@html parseMarkdownSafe(openedData.desc)}
</span>
</div>
<RealmLicense license={openedData.license}/>
<div class="flex justify-start gap-2 mt-2">
{#each openedData.tags as tag, i}
<div class="text-xs p-1 text-blue-400">{tag}</div>
{/each}
</div>
<div class="flex flex-wrap w-full flex-row gap-1 mt-2">
<span class="text-gray-500">
{language.chatAssumed.replace('{}', openedData.download.toString())}
</span>
<div class="border-l-selected border-l ml-1 mr-1"></div>
{#if openedData.viewScreen === 'emotion'}
<button class="text-gray-500 hover:text-green-500 transition-colors" on:click|stopPropagation={() => {alertNormal("This character includes emotion images")}}><SmileIcon /></button>
{/if}
{#if openedData.hasLore}
<button class="text-gray-500 hover:text-green-500 transition-colors" on:click|stopPropagation={() => {alertNormal("This character includes lorebook")}}><BookIcon /></button>
{/if}
</div>
</div>
<div class="flex flex-row-reverse gap-2">
<button class="text-gray-400 hover:text-red-500" on:click|stopPropagation={async () => {
const conf = await alertConfirm('Report this character?')
if(conf){
const report = await alertInput('Write a report text that would be sent to the admin')
const da = await fetch(hubURL + '/hub/report', {
method: "POST",
body: JSON.stringify({
id: openedData.id,
report: report
})
})
alertNormal(await da.text())
}
}}>
<FlagIcon />
</button>
{#if ($DataBase.account?.token?.split('-') ?? [])[1] === openedData.creator}
<button class="text-gray-400 hover:text-red-500" on:click|stopPropagation={async () => {
const conf = await alertConfirm('Do you want to remove this character from Realm?')
if(conf){
const da = await fetch(hubURL + '/hub/remove', {
method: "POST",
body: JSON.stringify({
id: openedData.id,
token: $DataBase.account?.token
})
})
alertNormal(await da.text())
}
}}>
<TrashIcon />
</button>
{/if}
<button class="text-gray-400 hover:text-green-500" on:click|stopPropagation={async () => {
await navigator.clipboard.writeText(`https://risuai.xyz/?realm=${openedData.id}`)
alertNormal("Copied to clipboard")
}}>
<PaperclipIcon />
</button>
<button class="bg-selected hover:ring flex-grow p-2 font-bold rounded-md mr-2" on:click={() => {
downloadRisuHub(openedData.id)
openedData = null
}}>
Download
</button>
</div>
</div>
</div>

View File

@@ -25,10 +25,19 @@
<TextAreaInput autocomplete="off" bind:value={char.creatorNotes} height={"20"} />
<span class="text-neutral-200">{language.tags}</span>
<span class="text-gray-400 text-sm">Tags to search your character easily. latin alphabets only. seperate by comma.</span>
<TextInput marginBottom placeholder="" bind:value={tags} on:input={() => {
<TextInput placeholder="" bind:value={tags} on:input={() => {
tags = tags.replace(/[^a-zA-Z,]/g, '').toLocaleLowerCase()
}} />
<div class="flex items-center flex-wrap">
<span class="text-neutral-200 mt-4">License</span>
<span class="text-gray-400 text-sm">You can choose license for the downloaders to limit the usages of your card.</span>
<SelectInput bind:value={license}>
<OptionInput value="">None</OptionInput>
{#each Object.keys(CCLicenseData) as ccl}
<OptionInput value={ccl}>{ccl} ({CCLicenseData[ccl][1]})</OptionInput>
{/each}
</SelectInput>
<div class="flex items-center flex-wrap mt-4">
<button class="bg-bgcolor p-2 rounded-lg" class:ring-1={!privateMode} on:click={() => {privateMode = false}}>🌏 Public</button>
<!-- <button class="bg-bgcolor p-2 rounded-lg ml-2" class:ring-1={privateMode} on:click={() => {privateMode = true}}>🔒 Private</button> -->
</div>
@@ -64,16 +73,21 @@
<script lang="ts">
import { XIcon } from "lucide-svelte";
import { language } from "src/lang";
import { alertError } from "src/ts/alert";
import { shareRisuHub } from "src/ts/characterCards";
import { alertError } from "src/ts/alert";
import { shareRisuHub } from "src/ts/characterCards";
import { DataBase, type character } from "src/ts/storage/database";
import TextInput from "../GUI/TextInput.svelte";
import TextAreaInput from "../GUI/TextAreaInput.svelte";
import Button from "../GUI/Button.svelte";
import TextInput from "../GUI/TextInput.svelte";
import TextAreaInput from "../GUI/TextAreaInput.svelte";
import Button from "../GUI/Button.svelte";
import SelectInput from "../GUI/SelectInput.svelte";
import { CCLicenseData } from "src/ts/copyright/license";
import { key } from "localforage";
import OptionInput from "../GUI/OptionInput.svelte";
export let close = () => {}
export let char:character
let tags=""
let privateMode = false
let nsfwMode = false
let license = ""
</script>

View File

@@ -3,7 +3,7 @@ import { sleep } from "./util"
import { language } from "../lang"
interface alertData{
type: 'error'| 'normal'|'none'|'ask'|'wait'|'selectChar'|'input'|'toast'|'wait2'|'markdown'|'select'|'login'
type: 'error'| 'normal'|'none'|'ask'|'wait'|'selectChar'|'input'|'toast'|'wait2'|'markdown'|'select'|'login'|'tos'
msg: string
}
@@ -78,6 +78,15 @@ export function alertToast(msg:string){
})
}
export function alertWait(msg:string){
alertStore.set({
'type': 'wait',
'msg': msg
})
}
export async function alertSelectChar(){
alertStore.set({
'type': 'selectChar',
@@ -111,6 +120,32 @@ export async function alertConfirm(msg:string){
return get(alertStore).msg === 'yes'
}
export async function alertTOS(){
// if(localStorage.getItem('tos') === 'true'){
// return true
// }
alertStore.set({
'type': 'tos',
'msg': 'tos'
})
while(true){
if (get(alertStore).type === 'none'){
break
}
await sleep(10)
}
if(get(alertStore).msg === 'yes'){
localStorage.setItem('tos', 'true')
return true
}
return false
}
export async function alertInput(msg:string){
alertStore.set({

View File

@@ -1,5 +1,5 @@
import { get } from "svelte/store"
import { alertConfirm, alertError, alertMd, alertNormal, alertSelect, alertStore } from "./alert"
import { alertConfirm, alertError, alertMd, alertNormal, alertSelect, alertStore, alertTOS } from "./alert"
import { DataBase, defaultSdDataFunc, type character, setDatabase, type customscript, type loreSettings, type loreBook } from "./storage/database"
import { checkNullish, selectMultipleFile, selectSingleFile, sleep } from "./util"
import { language } from "src/lang"
@@ -604,13 +604,14 @@ export async function shareRisuHub(char:character, arg:{
export type hubType = {
name:string
desc: string
download: number,
download: string,
id: string,
img: string
tags: string[],
viewScreen: "none" | "emotion" | "imggen"
hasLore:boolean
creator?:string
license:string
}
export async function getRisuHub(arg?:{
@@ -636,6 +637,9 @@ export async function getRisuHub(arg?:{
export async function downloadRisuHub(id:string) {
try {
if(!(await alertTOS())){
return
}
alertStore.set({
type: "wait",
msg: "Downloading..."

View File

@@ -0,0 +1,24 @@
import tippy from 'tippy.js'
import 'tippy.js/dist/tippy.css';
export const CCLicenseData = {
"CC BY 4.0": ["by", "Requires Attribution"],
"CC BY-NC 4.0": ["by-nc", "Requires Attribution and Non Commercial"],
"CC BY-NC-SA 4.0": ["by-nc-sa", "Requires Attribution, Non Commercial and Share Alike"],
"CC BY-SA 4.0": ["by-sa", "Requires Attribution and Share Alike"],
"CC BY-ND 4.0": ["by-nd", "Requires Attribution and No Derivatives"],
"CC BY-NC-ND 4.0": ["by-nc-nd", "Requires Attribution, Non Commercial and No Derivatives"],
}
export function tooltip(node:HTMLElement, tip:string) {
const instance = tippy(node, {
content: tip,
animation: 'fade',
arrow: true,
})
return {
destroy() {
instance.destroy()
}
};
}