refactor(webui): switch to SvelteKit | prettier everything
parent
e52560ae07
commit
8c1dc5388f
|
@ -28,15 +28,21 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
"svelte/valid-compile": ["error", { "ignoreWarnings": true }],
|
'svelte/valid-compile': ['error', { ignoreWarnings: true }],
|
||||||
"@typescript-eslint/no-explicit-any": "warn",
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
"@typescript-eslint/no-unused-vars": ["warn", {
|
'@typescript-eslint/no-unused-vars': [
|
||||||
argsIgnorePattern: "^_",
|
'warn',
|
||||||
varsIgnorePattern: "^_"
|
{
|
||||||
}],
|
argsIgnorePattern: '^_',
|
||||||
"no-console": ["error", {
|
varsIgnorePattern: '^_'
|
||||||
allow: ["debug", "warn", "error"]
|
}
|
||||||
}],
|
],
|
||||||
},
|
'no-console': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allow: ['debug', 'warn', 'error']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
*Psst — looking for a more complete solution? Check out [SvelteKit](https://kit.svelte.dev), the official framework for building web applications of all sizes, with a beautiful development experience and flexible filesystem-based routing.*
|
_Psst — looking for a more complete solution? Check out [SvelteKit](https://kit.svelte.dev), the official framework for building web applications of all sizes, with a beautiful development experience and flexible filesystem-based routing._
|
||||||
|
|
||||||
*Looking for a shareable component template instead? You can [use SvelteKit for that as well](https://kit.svelte.dev/docs#packaging) or the older [sveltejs/component-template](https://github.com/sveltejs/component-template)*
|
_Looking for a shareable component template instead? You can [use SvelteKit for that as well](https://kit.svelte.dev/docs#packaging) or the older [sveltejs/component-template](https://github.com/sveltejs/component-template)_
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -15,8 +15,7 @@ npx degit sveltejs/template svelte-app
|
||||||
cd svelte-app
|
cd svelte-app
|
||||||
```
|
```
|
||||||
|
|
||||||
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
|
_Note that you will need to have [Node.js](https://nodejs.org) installed._
|
||||||
|
|
||||||
|
|
||||||
## Get started
|
## Get started
|
||||||
|
|
||||||
|
@ -49,12 +48,11 @@ npm run build
|
||||||
|
|
||||||
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
|
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
|
||||||
|
|
||||||
|
|
||||||
## Single-page app mode
|
## Single-page app mode
|
||||||
|
|
||||||
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
|
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
|
||||||
|
|
||||||
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
|
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for _any_ path. You can make it so by editing the `"start"` command in package.json:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
"start": "sirv public --single"
|
"start": "sirv public --single"
|
||||||
|
|
|
@ -1,68 +1,68 @@
|
||||||
{
|
{
|
||||||
"name": "upend-kestrel",
|
"name": "upend-kestrel",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . && eslint .",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@types/eslint": "8.56.0",
|
"@types/eslint": "8.56.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.35.1",
|
"eslint-plugin-svelte": "^2.35.1",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
"svelte": "^4.2.7",
|
"svelte": "^4.2.7",
|
||||||
"svelte-check": "^3.6.0",
|
"svelte-check": "^3.6.0",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.0.3"
|
"vite": "^5.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ibm/plex": "^6.3.0",
|
"@ibm/plex": "^6.3.0",
|
||||||
"@recogito/annotorious": "^2.7.11",
|
"@recogito/annotorious": "^2.7.11",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/dompurify": "^3.0.5",
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/lodash": "^4.14",
|
"@types/lodash": "^4.14",
|
||||||
"@types/marked": "^4.3.2",
|
"@types/marked": "^4.3.2",
|
||||||
"@types/node": "^18.19.8",
|
"@types/node": "^18.19.8",
|
||||||
"@types/three": "^0.160.0",
|
"@types/three": "^0.160.0",
|
||||||
"@types/wavesurfer.js": "^6.0.12",
|
"@types/wavesurfer.js": "^6.0.12",
|
||||||
"@upnd/upend": "file:../tools/upend_js",
|
"@upnd/upend": "file:../tools/upend_js",
|
||||||
"@upnd/wasm-web": "file:../tools/upend_wasm/pkg-web",
|
"@upnd/wasm-web": "file:../tools/upend_wasm/pkg-web",
|
||||||
"boxicons": "^2.1.4",
|
"boxicons": "^2.1.4",
|
||||||
"d3": "^7.8.5",
|
"d3": "^7.8.5",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"dompurify": "^2.4.7",
|
"dompurify": "^2.4.7",
|
||||||
"filesize": "^8.0.7",
|
"filesize": "^8.0.7",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"i18next": "^22.5.1",
|
"i18next": "^22.5.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"marked": "^4.3.0",
|
"marked": "^4.3.0",
|
||||||
"match-sorter": "^6.3.1",
|
"match-sorter": "^6.3.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"sass": "^1.66.1",
|
"sass": "^1.66.1",
|
||||||
"sirv-cli": "^2.0.2",
|
"sirv-cli": "^2.0.2",
|
||||||
"sswr": "^1.11.0",
|
"sswr": "^1.11.0",
|
||||||
"svelte-i18next": "^1.2.2",
|
"svelte-i18next": "^1.2.2",
|
||||||
"three": "^0.147.0",
|
"three": "^0.147.0",
|
||||||
"vite-plugin-static-copy": "^0.13.1",
|
"vite-plugin-static-copy": "^0.13.1",
|
||||||
"wavesurfer.js": "^6.6.4"
|
"wavesurfer.js": "^6.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { UpEndApi } from "@upnd/upend";
|
import { UpEndApi } from '@upnd/upend';
|
||||||
import { UpEndWasmExtensionsWeb } from "@upnd/upend/wasm/web";
|
import { UpEndWasmExtensionsWeb } from '@upnd/upend/wasm/web';
|
||||||
import wasmURL from "@upnd/wasm-web/upend_wasm_bg.wasm?url";
|
import wasmURL from '@upnd/wasm-web/upend_wasm_bg.wasm?url';
|
||||||
|
|
||||||
const wasm = new UpEndWasmExtensionsWeb(wasmURL);
|
const wasm = new UpEndWasmExtensionsWeb(wasmURL);
|
||||||
export default new UpEndApi({ instanceUrl: "/", wasmExtensions: wasm });
|
export default new UpEndApi({ instanceUrl: '/', wasmExtensions: wasm });
|
||||||
|
|
|
@ -1,90 +1,90 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
import Icon from "./utils/Icon.svelte";
|
import Icon from './utils/Icon.svelte';
|
||||||
import Selector from "./utils/Selector.svelte";
|
import Selector from './utils/Selector.svelte';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
let selector: Selector;
|
let selector: Selector;
|
||||||
|
|
||||||
let editable = false;
|
let editable = false;
|
||||||
$: {
|
$: {
|
||||||
if (editable) {
|
if (editable) {
|
||||||
dispatch("editable");
|
dispatch('editable');
|
||||||
setTimeout(() => dispatch("editable"), 500); // once animation has finished
|
setTimeout(() => dispatch('editable'), 500); // once animation has finished
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$: editable && selector && selector.focus();
|
$: editable && selector && selector.focus();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="view"
|
class="view"
|
||||||
class:editable
|
class:editable
|
||||||
on:click={() => (editable = true)}
|
on:click={() => (editable = true)}
|
||||||
on:keydown={(ev) => {
|
on:keydown={(ev) => {
|
||||||
if (["Space", "Enter"].includes(ev.key)) editable = true;
|
if (['Space', 'Enter'].includes(ev.key)) editable = true;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon name="plus-circle" />
|
<Icon name="plus-circle" />
|
||||||
</div>
|
</div>
|
||||||
{#if editable}
|
{#if editable}
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<Selector
|
<Selector
|
||||||
bind:this={selector}
|
bind:this={selector}
|
||||||
types={["Address", "NewAddress", "Attribute"]}
|
types={['Address', 'NewAddress', 'Attribute']}
|
||||||
on:input={(ev) => {
|
on:input={(ev) => {
|
||||||
dispatch("input", ev.detail);
|
dispatch('input', ev.detail);
|
||||||
editable = false;
|
editable = false;
|
||||||
}}
|
}}
|
||||||
on:focus={(ev) => {
|
on:focus={(ev) => {
|
||||||
if (!ev.detail) editable = false;
|
if (!ev.detail) editable = false;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.view {
|
.view {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
background: var(--background-lighter);
|
background: var(--background-lighter);
|
||||||
color: var(--foreground-lighter);
|
color: var(--foreground-lighter);
|
||||||
border: 1px solid var(--foreground-lightest);
|
border: 1px solid var(--foreground-lightest);
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
transition:
|
transition:
|
||||||
opacity 0.3s,
|
opacity 0.3s,
|
||||||
width 0.5s,
|
width 0.5s,
|
||||||
min-width 0.5s;
|
min-width 0.5s;
|
||||||
|
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
min-width: 48px;
|
min-width: 48px;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&.editable {
|
&.editable {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.editable {
|
&.editable {
|
||||||
width: 18em;
|
width: 18em;
|
||||||
min-width: 18em;
|
min-width: 18em;
|
||||||
.icon {
|
.icon {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
transition: opacity 0.3s;
|
transition: opacity 0.3s;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,67 +1,58 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { UpEntry } from "@upnd/upend";
|
import type { UpEntry } from '@upnd/upend';
|
||||||
import { attributeLabels } from "../../util/labels";
|
import { attributeLabels } from '../../util/labels';
|
||||||
import UpObject from "./UpObject.svelte";
|
import UpObject from './UpObject.svelte';
|
||||||
export let resolve = true;
|
export let resolve = true;
|
||||||
|
|
||||||
export let entry: UpEntry;
|
export let entry: UpEntry;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
<div class="entity">
|
<div class="entity">
|
||||||
<UpObject
|
<UpObject plain link address={entry.entity} labels={resolve ? undefined : []} />
|
||||||
plain
|
</div>
|
||||||
link
|
<div class="attribute" title={entry.attribute}>
|
||||||
address={entry.entity}
|
{$attributeLabels[entry.attribute] || entry.attribute}
|
||||||
labels={resolve ? undefined : []}
|
</div>
|
||||||
/>
|
<div class="value value-{entry.value.t.toLowerCase()}">
|
||||||
</div>
|
{#if entry.value.t === 'Address'}
|
||||||
<div class="attribute" title={entry.attribute}>
|
<UpObject link address={entry.value.c} labels={resolve ? undefined : []} />
|
||||||
{$attributeLabels[entry.attribute] || entry.attribute}
|
{:else}
|
||||||
</div>
|
{entry.value.c}
|
||||||
<div class="value value-{entry.value.t.toLowerCase()}">
|
{/if}
|
||||||
{#if entry.value.t === "Address"}
|
</div>
|
||||||
<UpObject
|
|
||||||
link
|
|
||||||
address={entry.value.c}
|
|
||||||
labels={resolve ? undefined : []}
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
{entry.value.c}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.entry {
|
.entry {
|
||||||
border: 1px solid var(--foreground);
|
border: 1px solid var(--foreground);
|
||||||
background: var(--background-lighter);
|
background: var(--background-lighter);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 0.1em 0.5em 0.1em 0.25em;
|
padding: 0.1em 0.5em 0.1em 0.25em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribute {
|
.attribute {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
&::before {
|
&::before {
|
||||||
content: "→\00a0";
|
content: '→\00a0';
|
||||||
}
|
}
|
||||||
&::after {
|
&::after {
|
||||||
content: "\00a0→";
|
content: '\00a0→';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.value-value) {
|
:global(.value-value) {
|
||||||
font-family: var(--monospace-font);
|
font-family: var(--monospace-font);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,59 +1,59 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import HashBadge from "./HashBadge.svelte";
|
import HashBadge from './HashBadge.svelte';
|
||||||
import UpLink from "./UpLink.svelte";
|
import UpLink from './UpLink.svelte';
|
||||||
import BlobPreview from "./BlobPreview.svelte";
|
import BlobPreview from './BlobPreview.svelte';
|
||||||
import UpObject from "./UpObject.svelte";
|
import UpObject from './UpObject.svelte';
|
||||||
|
|
||||||
export let address: string;
|
export let address: string;
|
||||||
export let labels: string[] | undefined = undefined;
|
export let labels: string[] | undefined = undefined;
|
||||||
export let thumbnail = true;
|
export let thumbnail = true;
|
||||||
export let banner = true;
|
export let banner = true;
|
||||||
export let select = true;
|
export let select = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="upobjectcard">
|
<div class="upobjectcard">
|
||||||
<UpLink to={{ entity: address }} passthrough>
|
<UpLink to={{ entity: address }} passthrough>
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
{#if thumbnail}
|
{#if thumbnail}
|
||||||
<BlobPreview {address} />
|
<BlobPreview {address} />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="badge">
|
<div class="badge">
|
||||||
<HashBadge {address} />
|
<HashBadge {address} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<UpObject {address} {labels} {banner} {select} on:resolved />
|
<UpObject {address} {labels} {banner} {select} on:resolved />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</UpLink>
|
</UpLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.upobjectcard {
|
.upobjectcard {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
border: 1px solid var(--foreground-lighter);
|
border: 1px solid var(--foreground-lighter);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
max-height: 420px;
|
max-height: 420px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,48 +1,48 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Ellipsis from "../utils/Ellipsis.svelte";
|
import Ellipsis from '../utils/Ellipsis.svelte';
|
||||||
|
|
||||||
export let label: string;
|
export let label: string;
|
||||||
export let backpath: string[] = [];
|
export let backpath: string[] = [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="upobject-label">
|
<div class="upobject-label">
|
||||||
<Ellipsis value={label}>
|
<Ellipsis value={label}>
|
||||||
{#if backpath.length}
|
{#if backpath.length}
|
||||||
<span class="backpath">
|
<span class="backpath">
|
||||||
{#each backpath as component}
|
{#each backpath as component}
|
||||||
<span class="component">
|
<span class="component">
|
||||||
{component}
|
{component}
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="label">
|
<span class="label">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.upobject-label {
|
.upobject-label {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.backpath {
|
.backpath {
|
||||||
opacity: 0.66;
|
opacity: 0.66;
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
|
|
||||||
.component::after {
|
.component::after {
|
||||||
content: "∋";
|
content: '∋';
|
||||||
margin-left: 0.2em;
|
margin-left: 0.2em;
|
||||||
margin-right: 0.4em;
|
margin-right: 0.4em;
|
||||||
font-size: 0.66em;
|
font-size: 0.66em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -0.125em;
|
top: -0.125em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component:last-child::after {
|
.component:last-child::after {
|
||||||
content: "";
|
content: '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,106 +1,106 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from "../utils/Icon.svelte";
|
import Icon from '../utils/Icon.svelte';
|
||||||
import Jobs from "./Jobs.svelte";
|
import Jobs from './Jobs.svelte';
|
||||||
import Notifications from "./Notifications.svelte";
|
import Notifications from './Notifications.svelte';
|
||||||
import { i18n } from "../../i18n";
|
import { i18n } from '../../i18n';
|
||||||
|
|
||||||
let hidden = true;
|
let hidden = true;
|
||||||
let activeJobs: number;
|
let activeJobs: number;
|
||||||
$: togglable = activeJobs > 0 || !hidden;
|
$: togglable = activeJobs > 0 || !hidden;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<footer id="footer" class:hidden>
|
<footer id="footer" class:hidden>
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
<Notifications />
|
<Notifications />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="status"
|
class="status"
|
||||||
class:togglable
|
class:togglable
|
||||||
on:click={() => (hidden = !hidden)}
|
on:click={() => (hidden = !hidden)}
|
||||||
on:keydown={(ev) => {
|
on:keydown={(ev) => {
|
||||||
if (["Space", "Enter"].includes(ev.key)) hidden = !hidden;
|
if (['Space', 'Enter'].includes(ev.key)) hidden = !hidden;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
{#if activeJobs > 0}
|
{#if activeJobs > 0}
|
||||||
{$i18n.t("Active jobs:")} {activeJobs}
|
{$i18n.t('Active jobs:')} {activeJobs}
|
||||||
{:else}
|
{:else}
|
||||||
{$i18n.t("No active jobs.")}
|
{$i18n.t('No active jobs.')}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<Icon name="{hidden ? 'up' : 'down'}-arrow" />
|
<Icon name="{hidden ? 'up' : 'down'}-arrow" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="jobs">
|
<div class="jobs">
|
||||||
<Jobs bind:active={activeJobs} />
|
<Jobs bind:active={activeJobs} />
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
footer {
|
footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
border-top: 1px solid var(--foreground-lighter);
|
border-top: 1px solid var(--foreground-lighter);
|
||||||
|
|
||||||
transition: 0.7s bottom ease;
|
transition: 0.7s bottom ease;
|
||||||
|
|
||||||
--height: calc(100vh / 6);
|
--height: calc(100vh / 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
footer.hidden {
|
footer.hidden {
|
||||||
bottom: calc(var(--height) * -1);
|
bottom: calc(var(--height) * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:not(.togglable) {
|
&:not(.togglable) {
|
||||||
cursor: unset;
|
cursor: unset;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.66;
|
opacity: 0.66;
|
||||||
}
|
}
|
||||||
|
|
||||||
transition: opacity 0.7s ease;
|
transition: opacity 0.7s ease;
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notifications,
|
.notifications,
|
||||||
.jobs {
|
.jobs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.notifications > *:first-child) {
|
:global(.notifications > *:first-child) {
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jobs {
|
.jobs {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: var(--height);
|
height: var(--height);
|
||||||
|
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
|
|
||||||
background: var(--background-lighter);
|
background: var(--background-lighter);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
section.labelborder {
|
section.labelborder {
|
||||||
margin-top: 0.66rem;
|
margin-top: 0.66rem;
|
||||||
|
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
border-bottom: 1px solid var(--foreground);
|
border-bottom: 1px solid var(--foreground);
|
||||||
padding-bottom: 0.33rem;
|
padding-bottom: 0.33rem;
|
||||||
margin-bottom: 0.33rem;
|
margin-bottom: 0.33rem;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let value: string;
|
export let value: string;
|
||||||
export let title: string | undefined = undefined;
|
export let title: string | undefined = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="ellipsis" title={title || value}><slot>{value}</slot></div>
|
<div class="ellipsis" title={title || value}><slot>{value}</slot></div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.ellipsis {
|
.ellipsis {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,88 +1,88 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from "./Icon.svelte";
|
import Icon from './Icon.svelte';
|
||||||
|
|
||||||
export let name: string;
|
export let name: string;
|
||||||
export let active = false;
|
export let active = false;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let title: string | undefined = undefined;
|
export let title: string | undefined = undefined;
|
||||||
export let outline = false;
|
export let outline = false;
|
||||||
export let subdued = false;
|
export let subdued = false;
|
||||||
export let small = false;
|
export let small = false;
|
||||||
export let plain = false;
|
export let plain = false;
|
||||||
export let color: string | undefined = "var(--active-color, var(--primary))";
|
export let color: string | undefined = 'var(--active-color, var(--primary))';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
on:click
|
on:click
|
||||||
class:active
|
class:active
|
||||||
class:outline
|
class:outline
|
||||||
class:subdued
|
class:subdued
|
||||||
class:small
|
class:small
|
||||||
class:plain
|
class:plain
|
||||||
{disabled}
|
{disabled}
|
||||||
{title}
|
{title}
|
||||||
style="--color: {color}"
|
style="--color: {color}"
|
||||||
>
|
>
|
||||||
<Icon {name} />
|
<Icon {name} />
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
button {
|
button {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
opacity: 0.66;
|
opacity: 0.66;
|
||||||
|
|
||||||
&.plain {
|
&.plain {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
transition:
|
transition:
|
||||||
opacity 0.2s,
|
opacity 0.2s,
|
||||||
color 0.2s,
|
color 0.2s,
|
||||||
border-color 0.2s;
|
border-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.subdued {
|
button.subdued {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.outline {
|
.outline {
|
||||||
border: 1px solid var(--foreground);
|
border: 1px solid var(--foreground);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
padding: 0.25em 1em;
|
padding: 0.25em 1em;
|
||||||
&.small {
|
&.small {
|
||||||
padding: 0.1em 0.8em;
|
padding: 0.1em 0.8em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.active,
|
.active,
|
||||||
button:hover {
|
button:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
border-color: var(--color);
|
border-color: var(--color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
color: gray;
|
color: gray;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
font-size: 0.5em;
|
font-size: 0.5em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 0.2em;
|
margin-top: 0.2em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,63 +1,63 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let hide = false;
|
export let hide = false;
|
||||||
let hidden = true;
|
let hidden = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="labelborder" class:hide class:hidden>
|
<section class="labelborder" class:hide class:hidden>
|
||||||
<header
|
<header
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (hide) {
|
if (hide) {
|
||||||
hidden = !hidden;
|
hidden = !hidden;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
on:keydown={(ev) => {
|
on:keydown={(ev) => {
|
||||||
if (["Space", "Enter"].includes(ev.key) && hide) hidden = !hidden;
|
if (['Space', 'Enter'].includes(ev.key) && hide) hidden = !hidden;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<slot name="header-full">
|
<slot name="header-full">
|
||||||
<h3><slot name="header" /></h3>
|
<h3><slot name="header" /></h3>
|
||||||
</slot>
|
</slot>
|
||||||
</header>
|
</header>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
section.labelborder {
|
section.labelborder {
|
||||||
margin-top: 0.66rem;
|
margin-top: 0.66rem;
|
||||||
|
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
border-bottom: 1px solid var(--foreground);
|
border-bottom: 1px solid var(--foreground);
|
||||||
padding-bottom: 0.33rem;
|
padding-bottom: 0.33rem;
|
||||||
margin-bottom: 0.33rem;
|
margin-bottom: 0.33rem;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hide {
|
&.hide {
|
||||||
header {
|
header {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
&.hidden {
|
&.hidden {
|
||||||
opacity: 0.66;
|
opacity: 0.66;
|
||||||
|
|
||||||
header {
|
header {
|
||||||
border-bottom-width: 0.5px;
|
border-bottom-width: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,71 +1,67 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let value: number | undefined = undefined;
|
export let value: number | undefined = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="progress-bar" class:indeterminate={value == undefined} style="--value: {value}%">
|
||||||
class="progress-bar"
|
<div class="value" />
|
||||||
class:indeterminate={value == undefined}
|
<div class="label">
|
||||||
style="--value: {value}%"
|
<slot>
|
||||||
>
|
{value ? Math.round(value) : '?'}%
|
||||||
<div class="value" />
|
</slot>
|
||||||
<div class="label">
|
</div>
|
||||||
<slot>
|
|
||||||
{value ? Math.round(value) : "?"}%
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
background: white;
|
background: white;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: var(--value, 100%);
|
width: var(--value, 100%);
|
||||||
transition: width 0.2s ease;
|
transition: width 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar,
|
.progress-bar,
|
||||||
.value {
|
.value {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
color: white;
|
color: white;
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 0;
|
top: 0;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
mix-blend-mode: difference;
|
mix-blend-mode: difference;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar.indeterminate {
|
.progress-bar.indeterminate {
|
||||||
.value {
|
.value {
|
||||||
animation-name: indeterminate;
|
animation-name: indeterminate;
|
||||||
animation-duration: 2s;
|
animation-duration: 2s;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
animation-timing-function: ease-in-out;
|
animation-timing-function: ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes indeterminate {
|
@keyframes indeterminate {
|
||||||
0% {
|
0% {
|
||||||
background-color: var(--primary);
|
background-color: var(--primary);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-color: var(--primary-lighter);
|
background-color: var(--primary-lighter);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-color: var(--primary);
|
background-color: var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let centered: boolean | string = false;
|
export let centered: boolean | string = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="spinner lds-ripple"
|
class="spinner lds-ripple"
|
||||||
class:centered={Boolean(centered)}
|
class:centered={Boolean(centered)}
|
||||||
class:absolute-centered={centered == "absolute"}
|
class:absolute-centered={centered == 'absolute'}
|
||||||
>
|
>
|
||||||
<i class="bx bx-loader bx-spin" />
|
<i class="bx bx-loader bx-spin" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.spinner {
|
.spinner {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
.spinner.centered {
|
.spinner.centered {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner.absolute-centered {
|
.spinner.absolute-centered {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,36 +1,34 @@
|
||||||
export const DEBUG = {
|
export const DEBUG = {
|
||||||
imageQueueHalt: Boolean(localStorage.getItem("DEBUG:IMAGEHALT")),
|
imageQueueHalt: Boolean(localStorage.getItem('DEBUG:IMAGEHALT')),
|
||||||
mockJobs: parseInt(localStorage.getItem("DEBUG:MOCK:JOBS") || "0"),
|
mockJobs: parseInt(localStorage.getItem('DEBUG:MOCK:JOBS') || '0'),
|
||||||
mockNotifications: parseInt(
|
mockNotifications: parseInt(localStorage.getItem('DEBUG:MOCK:NOTIFICATIONS') || '0')
|
||||||
localStorage.getItem("DEBUG:MOCK:NOTIFICATIONS") || "0",
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function lipsum(length: number): string {
|
export function lipsum(length: number): string {
|
||||||
const words = [
|
const words = [
|
||||||
"lorem",
|
'lorem',
|
||||||
"ipsum",
|
'ipsum',
|
||||||
"dolor",
|
'dolor',
|
||||||
"sit",
|
'sit',
|
||||||
"amet",
|
'amet',
|
||||||
"consectetur",
|
'consectetur',
|
||||||
"adipiscing",
|
'adipiscing',
|
||||||
"elit",
|
'elit',
|
||||||
"sed",
|
'sed',
|
||||||
"do",
|
'do',
|
||||||
"eiusmod",
|
'eiusmod',
|
||||||
"tempor",
|
'tempor',
|
||||||
"incididunt",
|
'incididunt',
|
||||||
"ut",
|
'ut',
|
||||||
"labore",
|
'labore',
|
||||||
"et",
|
'et',
|
||||||
"dolore",
|
'dolore',
|
||||||
"magna",
|
'magna',
|
||||||
"aliqua",
|
'aliqua'
|
||||||
];
|
];
|
||||||
const result = ["lorem", "ipsum"];
|
const result = ['lorem', 'ipsum'];
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
result.push(words[Math.floor(Math.random() * words.length)]);
|
result.push(words[Math.floor(Math.random() * words.length)]);
|
||||||
}
|
}
|
||||||
return result.join(" ");
|
return result.join(' ');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"FILE_MIME": "MIME type",
|
"FILE_MIME": "MIME type",
|
||||||
"FILE_SIZE": "File size",
|
"FILE_SIZE": "File size",
|
||||||
"ADDED": "Added at",
|
"ADDED": "Added at",
|
||||||
"LAST_VISITED": "Last visited at",
|
"LAST_VISITED": "Last visited at",
|
||||||
"NUM_VISITED": "Times visited",
|
"NUM_VISITED": "Times visited",
|
||||||
"ATTR_LABEL": "Label",
|
"ATTR_LABEL": "Label",
|
||||||
"IS": "Type",
|
"IS": "Type",
|
||||||
"TYPE": "Type ID",
|
"TYPE": "Type ID",
|
||||||
"MEDIA_DURATION": "Duration"
|
"MEDIA_DURATION": "Duration"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import i18next from "i18next";
|
import i18next from 'i18next';
|
||||||
import { createI18nStore } from "svelte-i18next";
|
import { createI18nStore } from 'svelte-i18next';
|
||||||
import en from "./en.json";
|
import en from './en.json';
|
||||||
|
|
||||||
i18next.init({
|
i18next.init({
|
||||||
lng: "en",
|
lng: 'en',
|
||||||
resources: {
|
resources: {
|
||||||
en,
|
en
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const i18n = createI18nStore(i18next);
|
export const i18n = createI18nStore(i18next);
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
import mitt from "mitt";
|
import mitt from 'mitt';
|
||||||
|
|
||||||
type NotifyEvents = {
|
type NotifyEvents = {
|
||||||
notification: UpNotification;
|
notification: UpNotification;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpNotificationLevel = "info" | "warning" | "error";
|
export type UpNotificationLevel = 'info' | 'warning' | 'error';
|
||||||
|
|
||||||
export interface INotification {
|
export interface INotification {
|
||||||
id: string;
|
id: string;
|
||||||
content: string;
|
content: string;
|
||||||
level: UpNotificationLevel;
|
level: UpNotificationLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpNotification implements INotification {
|
export class UpNotification implements INotification {
|
||||||
id: string;
|
id: string;
|
||||||
content: string;
|
content: string;
|
||||||
level: UpNotificationLevel;
|
level: UpNotificationLevel;
|
||||||
|
|
||||||
constructor(content: string, level?: UpNotificationLevel) {
|
constructor(content: string, level?: UpNotificationLevel) {
|
||||||
this.id = String(Math.random());
|
this.id = String(Math.random());
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.level = level || "info";
|
this.level = level || 'info';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const notify = mitt<NotifyEvents>();
|
export const notify = mitt<NotifyEvents>();
|
||||||
|
|
|
@ -1,92 +1,83 @@
|
||||||
@use "colors";
|
@use 'colors';
|
||||||
@use "sass:color";
|
@use 'sass:color';
|
||||||
|
|
||||||
@mixin rebase(
|
@mixin rebase($rebase03, $rebase02, $rebase01, $rebase00, $rebase0, $rebase1, $rebase2, $rebase3) {
|
||||||
$rebase03,
|
--foreground: #{$rebase0};
|
||||||
$rebase02,
|
--foreground-lighter: #{$rebase1};
|
||||||
$rebase01,
|
--foreground-lightest: #{$rebase2};
|
||||||
$rebase00,
|
--background: #{$rebase03};
|
||||||
$rebase0,
|
--background-lighter: #{$rebase02};
|
||||||
$rebase1,
|
--background-lightest: #{color.adjust($rebase02, $lightness: 1.5%)};
|
||||||
$rebase2,
|
--primary: #{colors.$blue};
|
||||||
$rebase3
|
--primary-lighter: #{color.adjust(colors.$blue, $lightness: 25%)};
|
||||||
) {
|
|
||||||
--foreground: #{$rebase0};
|
|
||||||
--foreground-lighter: #{$rebase1};
|
|
||||||
--foreground-lightest: #{$rebase2};
|
|
||||||
--background: #{$rebase03};
|
|
||||||
--background-lighter: #{$rebase02};
|
|
||||||
--background-lightest: #{color.adjust($rebase02, $lightness: 1.5%)};
|
|
||||||
--primary: #{colors.$blue};
|
|
||||||
--primary-lighter: #{color.adjust(colors.$blue, $lightness: 25%)};
|
|
||||||
|
|
||||||
background-color: $rebase03;
|
background-color: $rebase03;
|
||||||
color: $rebase0;
|
color: $rebase0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin accentize($accent) {
|
@mixin accentize($accent) {
|
||||||
a,
|
a,
|
||||||
a:active,
|
a:active,
|
||||||
a:visited,
|
a:visited,
|
||||||
code.url {
|
code.url {
|
||||||
color: $accent;
|
color: $accent;
|
||||||
}
|
}
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
color: $accent;
|
color: $accent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
html {
|
||||||
@include rebase(
|
@include rebase(
|
||||||
colors.$base3,
|
colors.$base3,
|
||||||
colors.$base2,
|
colors.$base2,
|
||||||
colors.$base1,
|
colors.$base1,
|
||||||
colors.$base0,
|
colors.$base0,
|
||||||
colors.$base00,
|
colors.$base00,
|
||||||
colors.$base01,
|
colors.$base01,
|
||||||
colors.$base02,
|
colors.$base02,
|
||||||
colors.$base03
|
colors.$base03
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@include rebase(
|
@include rebase(
|
||||||
colors.$base03,
|
colors.$base03,
|
||||||
colors.$base02,
|
colors.$base02,
|
||||||
colors.$base01,
|
colors.$base01,
|
||||||
colors.$base00,
|
colors.$base00,
|
||||||
colors.$base0,
|
colors.$base0,
|
||||||
colors.$base1,
|
colors.$base1,
|
||||||
colors.$base2,
|
colors.$base2,
|
||||||
colors.$base3
|
colors.$base3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
color: var(--foreground-lighter);
|
color: var(--foreground-lighter);
|
||||||
border-color: var(--foreground);
|
border-color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
a,
|
a,
|
||||||
a:active,
|
a:active,
|
||||||
a:visited {
|
a:visited {
|
||||||
color: var(--foreground-lighter);
|
color: var(--foreground-lighter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +1,68 @@
|
||||||
@use "sass:color";
|
@use 'sass:color';
|
||||||
@use "colors";
|
@use 'colors';
|
||||||
|
|
||||||
html {
|
html {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
background: var(--background-lighter);
|
background: var(--background-lighter);
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
font-family: var(--default-font);
|
font-family: var(--default-font);
|
||||||
|
|
||||||
border: 1px solid var(--foreground-lighter);
|
border: 1px solid var(--foreground-lighter);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
border: 1px solid var(--foreground);
|
border: 1px solid var(--foreground);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
background: var(--background-lighter);
|
background: var(--background-lighter);
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
|
|
||||||
padding: 0.25em 1em;
|
padding: 0.25em 1em;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled,
|
&.disabled,
|
||||||
&:disabled {
|
&:disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mark-entity::first-letter,
|
.mark-entity::first-letter,
|
||||||
.mark-entity *::first-letter {
|
.mark-entity *::first-letter {
|
||||||
color: color.scale(color.mix(colors.$base1, colors.$red), $saturation: -33%);
|
color: color.scale(color.mix(colors.$base1, colors.$red), $saturation: -33%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mark-attribute::first-letter,
|
.mark-attribute::first-letter,
|
||||||
.mark-attribute *::first-letter {
|
.mark-attribute *::first-letter {
|
||||||
color: color.mix(colors.$base1, colors.$green);
|
color: color.mix(colors.$base1, colors.$green);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mark-value::first-letter,
|
.mark-value::first-letter,
|
||||||
.mark-value *::first-letter {
|
.mark-value *::first-letter {
|
||||||
color: color.mix(colors.$base1, colors.$blue);
|
color: color.mix(colors.$base1, colors.$blue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
@import "node_modules/@ibm/plex/scss/ibm-plex.scss";
|
@import 'node_modules/@ibm/plex/scss/ibm-plex.scss';
|
||||||
|
|
||||||
html {
|
html {
|
||||||
--default-font: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif;
|
--default-font: 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
|
||||||
--monospace-font: "IBM Plex Mono", "Menlo", "DejaVu Sans Mono", "Consolas",
|
--monospace-font: 'IBM Plex Mono', 'Menlo', 'DejaVu Sans Mono', 'Consolas', 'Inconsolata', 'Hack',
|
||||||
"Inconsolata", "Hack", monospace;
|
monospace;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
font-family: var(--default-font);
|
font-family: var(--default-font);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
@use "normalize.css/normalize.css";
|
@use 'normalize.css/normalize.css';
|
||||||
@use "colors-app";
|
@use 'colors-app';
|
||||||
@use "fonts";
|
@use 'fonts';
|
||||||
@use "common";
|
@use 'common';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
height: calc(100vh - 2rem);
|
height: calc(100vh - 2rem);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-queued,
|
.image-queued,
|
||||||
.image-loading {
|
.image-loading {
|
||||||
animation: gradient 1.5s ease infinite;
|
animation: gradient 1.5s ease infinite;
|
||||||
|
|
||||||
color: transparent; // Hide alt-text
|
color: transparent; // Hide alt-text
|
||||||
|
|
||||||
@keyframes gradient {
|
@keyframes gradient {
|
||||||
0% {
|
0% {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-color: rgba(255, 255, 255, 0.4);
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-queued {
|
.image-queued {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
import type { IValue } from "@upnd/upend/types";
|
import type { IValue } from '@upnd/upend/types';
|
||||||
|
|
||||||
export type WidgetChange =
|
export type WidgetChange =
|
||||||
| AttributeCreate
|
| AttributeCreate
|
||||||
| AttributeUpdate
|
| AttributeUpdate
|
||||||
| AttributeDelete
|
| AttributeDelete
|
||||||
| EntryInAdd
|
| EntryInAdd
|
||||||
| EntryInDelete;
|
| EntryInDelete;
|
||||||
|
|
||||||
export interface AttributeCreate {
|
export interface AttributeCreate {
|
||||||
type: "create";
|
type: 'create';
|
||||||
attribute: string;
|
attribute: string;
|
||||||
value: IValue;
|
value: IValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttributeUpdate {
|
export interface AttributeUpdate {
|
||||||
type: "update";
|
type: 'update';
|
||||||
attribute: string;
|
attribute: string;
|
||||||
value: IValue;
|
value: IValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttributeDelete {
|
export interface AttributeDelete {
|
||||||
type: "delete";
|
type: 'delete';
|
||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntryInAdd {
|
export interface EntryInAdd {
|
||||||
type: "entry-add";
|
type: 'entry-add';
|
||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntryInDelete {
|
export interface EntryInDelete {
|
||||||
type: "entry-delete";
|
type: 'entry-delete';
|
||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Readable } from "svelte/store";
|
import type { Readable } from 'svelte/store';
|
||||||
|
|
||||||
export interface BrowseContext {
|
export interface BrowseContext {
|
||||||
index: Readable<number>;
|
index: Readable<number>;
|
||||||
addresses: Readable<string[]>;
|
addresses: Readable<string[]>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,37 @@
|
||||||
import type { EntityInfo } from "@upnd/upend/types";
|
import type { EntityInfo } from '@upnd/upend/types';
|
||||||
import type { UpObject } from "@upnd/upend";
|
import type { UpObject } from '@upnd/upend';
|
||||||
import { ATTR_IN } from "@upnd/upend/constants";
|
import { ATTR_IN } from '@upnd/upend/constants';
|
||||||
|
|
||||||
export function getTypes(entity: UpObject, entityInfo: EntityInfo) {
|
export function getTypes(entity: UpObject, entityInfo: EntityInfo) {
|
||||||
const mimeType = String(entity?.get("FILE_MIME"));
|
const mimeType = String(entity?.get('FILE_MIME'));
|
||||||
|
|
||||||
const video =
|
const video =
|
||||||
["video", "application/x-matroska"].some((p) => mimeType.startsWith(p)) ||
|
['video', 'application/x-matroska'].some((p) => mimeType.startsWith(p)) ||
|
||||||
entity?.identify().some((l) => l.endsWith(".avi"));
|
entity?.identify().some((l) => l.endsWith('.avi'));
|
||||||
const audio =
|
const audio =
|
||||||
(["audio", "application/x-riff"].some((p) => mimeType.startsWith(p)) &&
|
(['audio', 'application/x-riff'].some((p) => mimeType.startsWith(p)) && !video) ||
|
||||||
!video) ||
|
['.ogg', '.mp3', '.wav'].some((suffix) =>
|
||||||
[".ogg", ".mp3", ".wav"].some(
|
entity?.identify().some((l) => l.toLowerCase().endsWith(suffix))
|
||||||
(suffix) =>
|
);
|
||||||
entity?.identify().some((l) => l.toLowerCase().endsWith(suffix)),
|
const image = mimeType.startsWith('image');
|
||||||
);
|
const text = mimeType.startsWith('text');
|
||||||
const image = mimeType.startsWith("image");
|
const pdf = mimeType.startsWith('application/pdf');
|
||||||
const text = mimeType.startsWith("text");
|
const model = mimeType?.startsWith('model') || entity?.identify().some((l) => l.endsWith('.stl'));
|
||||||
const pdf = mimeType.startsWith("application/pdf");
|
const web = entityInfo?.t == 'Url';
|
||||||
const model =
|
const fragment = Boolean(entity?.get('ANNOTATES'));
|
||||||
mimeType?.startsWith("model") ||
|
|
||||||
entity?.identify().some((l) => l.endsWith(".stl"));
|
|
||||||
const web = entityInfo?.t == "Url";
|
|
||||||
const fragment = Boolean(entity?.get("ANNOTATES"));
|
|
||||||
|
|
||||||
const group = entity?.backlinks.some((e) => e.attribute == ATTR_IN);
|
const group = entity?.backlinks.some((e) => e.attribute == ATTR_IN);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mimeType,
|
mimeType,
|
||||||
audio,
|
audio,
|
||||||
video,
|
video,
|
||||||
image,
|
image,
|
||||||
text,
|
text,
|
||||||
pdf,
|
pdf,
|
||||||
model,
|
model,
|
||||||
web,
|
web,
|
||||||
fragment,
|
fragment,
|
||||||
group,
|
group
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
export function updateTitle(route?: string, title?: string) {
|
export function updateTitle(route?: string, title?: string) {
|
||||||
let newTitle = "UpEnd";
|
let newTitle = 'UpEnd';
|
||||||
if (route?.length) {
|
if (route?.length) {
|
||||||
newTitle += ` | ${route}`;
|
newTitle += ` | ${route}`;
|
||||||
}
|
}
|
||||||
if (title?.length) {
|
if (title?.length) {
|
||||||
newTitle += ` - ${title}`;
|
newTitle += ` - ${title}`;
|
||||||
}
|
}
|
||||||
document.title = newTitle;
|
document.title = newTitle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
import { sveltekit } from "@sveltejs/kit/vite";
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from 'vite';
|
||||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||||
import * as path from "path";
|
import * as path from 'path';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
sveltekit(),
|
sveltekit(),
|
||||||
viteStaticCopy({
|
viteStaticCopy({
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
src: path.join(__dirname, "node_modules/boxicons", "fonts"),
|
src: path.join(__dirname, 'node_modules/boxicons', 'fonts'),
|
||||||
dest: path.resolve(__dirname, "dist/vendor/boxicons"),
|
dest: path.resolve(__dirname, 'dist/vendor/boxicons')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: path.join(__dirname, "node_modules/boxicons", "css"),
|
src: path.join(__dirname, 'node_modules/boxicons', 'css'),
|
||||||
dest: path.resolve(__dirname, "dist/vendor/boxicons"),
|
dest: path.resolve(__dirname, 'dist/vendor/boxicons')
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
}),
|
})
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
'/api': {
|
||||||
target: "http://localhost:8093/",
|
target: 'http://localhost:8093/'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue