refactor(webui): switch to SvelteKit | prettier everything

develop
Tomáš Mládek 2024-01-22 22:58:55 +01:00
parent e52560ae07
commit 8c1dc5388f
28 changed files with 855 additions and 879 deletions

View File

@ -29,14 +29,20 @@ module.exports = {
}
],
rules: {
"svelte/valid-compile": ["error", { "ignoreWarnings": true }],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["warn", {
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}],
"no-console": ["error", {
allow: ["debug", "warn", "error"]
}],
},
'svelte/valid-compile': ['error', { ignoreWarnings: true }],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}
],
'no-console': [
'error',
{
allow: ['debug', 'warn', 'error']
}
]
}
};

View File

@ -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
```
*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
@ -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).
## 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.
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
"start": "sirv public --single"

View File

@ -1,6 +1,6 @@
import { UpEndApi } from "@upnd/upend";
import { UpEndWasmExtensionsWeb } from "@upnd/upend/wasm/web";
import wasmURL from "@upnd/wasm-web/upend_wasm_bg.wasm?url";
import { UpEndApi } from '@upnd/upend';
import { UpEndWasmExtensionsWeb } from '@upnd/upend/wasm/web';
import wasmURL from '@upnd/wasm-web/upend_wasm_bg.wasm?url';
const wasm = new UpEndWasmExtensionsWeb(wasmURL);
export default new UpEndApi({ instanceUrl: "/", wasmExtensions: wasm });
export default new UpEndApi({ instanceUrl: '/', wasmExtensions: wasm });

View File

@ -1,16 +1,16 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { createEventDispatcher } from 'svelte';
import Icon from "./utils/Icon.svelte";
import Selector from "./utils/Selector.svelte";
import Icon from './utils/Icon.svelte';
import Selector from './utils/Selector.svelte';
const dispatch = createEventDispatcher();
let selector: Selector;
let editable = false;
$: {
if (editable) {
dispatch("editable");
setTimeout(() => dispatch("editable"), 500); // once animation has finished
dispatch('editable');
setTimeout(() => dispatch('editable'), 500); // once animation has finished
}
}
$: editable && selector && selector.focus();
@ -21,7 +21,7 @@
class:editable
on:click={() => (editable = true)}
on:keydown={(ev) => {
if (["Space", "Enter"].includes(ev.key)) editable = true;
if (['Space', 'Enter'].includes(ev.key)) editable = true;
}}
>
<div class="icon">
@ -31,9 +31,9 @@
<div class="controls">
<Selector
bind:this={selector}
types={["Address", "NewAddress", "Attribute"]}
types={['Address', 'NewAddress', 'Attribute']}
on:input={(ev) => {
dispatch("input", ev.detail);
dispatch('input', ev.detail);
editable = false;
}}
on:focus={(ev) => {

View File

@ -1,7 +1,7 @@
<script lang="ts">
import type { UpEntry } from "@upnd/upend";
import { attributeLabels } from "../../util/labels";
import UpObject from "./UpObject.svelte";
import type { UpEntry } from '@upnd/upend';
import { attributeLabels } from '../../util/labels';
import UpObject from './UpObject.svelte';
export let resolve = true;
export let entry: UpEntry;
@ -9,23 +9,14 @@
<div class="entry">
<div class="entity">
<UpObject
plain
link
address={entry.entity}
labels={resolve ? undefined : []}
/>
<UpObject plain link address={entry.entity} labels={resolve ? undefined : []} />
</div>
<div class="attribute" title={entry.attribute}>
{$attributeLabels[entry.attribute] || entry.attribute}
</div>
<div class="value value-{entry.value.t.toLowerCase()}">
{#if entry.value.t === "Address"}
<UpObject
link
address={entry.value.c}
labels={resolve ? undefined : []}
/>
{#if entry.value.t === 'Address'}
<UpObject link address={entry.value.c} labels={resolve ? undefined : []} />
{:else}
{entry.value.c}
{/if}
@ -54,10 +45,10 @@
text-align: center;
font-weight: 300;
&::before {
content: "→\00a0";
content: '→\00a0';
}
&::after {
content: "\00a0→";
content: '\00a0→';
}
}

View File

@ -1,8 +1,8 @@
<script lang="ts">
import HashBadge from "./HashBadge.svelte";
import UpLink from "./UpLink.svelte";
import BlobPreview from "./BlobPreview.svelte";
import UpObject from "./UpObject.svelte";
import HashBadge from './HashBadge.svelte';
import UpLink from './UpLink.svelte';
import BlobPreview from './BlobPreview.svelte';
import UpObject from './UpObject.svelte';
export let address: string;
export let labels: string[] | undefined = undefined;

View File

@ -1,5 +1,5 @@
<script lang="ts">
import Ellipsis from "../utils/Ellipsis.svelte";
import Ellipsis from '../utils/Ellipsis.svelte';
export let label: string;
export let backpath: string[] = [];
@ -32,7 +32,7 @@
margin-right: 0.25em;
.component::after {
content: "∋";
content: '∋';
margin-left: 0.2em;
margin-right: 0.4em;
font-size: 0.66em;
@ -42,7 +42,7 @@
}
.component:last-child::after {
content: "";
content: '';
}
}
</style>

View File

@ -1,8 +1,8 @@
<script lang="ts">
import Icon from "../utils/Icon.svelte";
import Jobs from "./Jobs.svelte";
import Notifications from "./Notifications.svelte";
import { i18n } from "../../i18n";
import Icon from '../utils/Icon.svelte';
import Jobs from './Jobs.svelte';
import Notifications from './Notifications.svelte';
import { i18n } from '../../i18n';
let hidden = true;
let activeJobs: number;
@ -18,14 +18,14 @@
class:togglable
on:click={() => (hidden = !hidden)}
on:keydown={(ev) => {
if (["Space", "Enter"].includes(ev.key)) hidden = !hidden;
if (['Space', 'Enter'].includes(ev.key)) hidden = !hidden;
}}
>
<div class="info">
{#if activeJobs > 0}
{$i18n.t("Active jobs:")} {activeJobs}
{$i18n.t('Active jobs:')} {activeJobs}
{:else}
{$i18n.t("No active jobs.")}
{$i18n.t('No active jobs.')}
{/if}
</div>
<div class="icons">

View File

@ -1,5 +1,5 @@
<script lang="ts">
import Icon from "./Icon.svelte";
import Icon from './Icon.svelte';
export let name: string;
export let active = false;
@ -9,7 +9,7 @@
export let subdued = false;
export let small = 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>
<button

View File

@ -11,7 +11,7 @@
}
}}
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">

View File

@ -2,15 +2,11 @@
export let value: number | undefined = undefined;
</script>
<div
class="progress-bar"
class:indeterminate={value == undefined}
style="--value: {value}%"
>
<div class="progress-bar" class:indeterminate={value == undefined} style="--value: {value}%">
<div class="value" />
<div class="label">
<slot>
{value ? Math.round(value) : "?"}%
{value ? Math.round(value) : '?'}%
</slot>
</div>
</div>

View File

@ -5,7 +5,7 @@
<div
class="spinner lds-ripple"
class:centered={Boolean(centered)}
class:absolute-centered={centered == "absolute"}
class:absolute-centered={centered == 'absolute'}
>
<i class="bx bx-loader bx-spin" />
</div>

View File

@ -1,36 +1,34 @@
export const DEBUG = {
imageQueueHalt: Boolean(localStorage.getItem("DEBUG:IMAGEHALT")),
mockJobs: parseInt(localStorage.getItem("DEBUG:MOCK:JOBS") || "0"),
mockNotifications: parseInt(
localStorage.getItem("DEBUG:MOCK:NOTIFICATIONS") || "0",
),
imageQueueHalt: Boolean(localStorage.getItem('DEBUG:IMAGEHALT')),
mockJobs: parseInt(localStorage.getItem('DEBUG:MOCK:JOBS') || '0'),
mockNotifications: parseInt(localStorage.getItem('DEBUG:MOCK:NOTIFICATIONS') || '0')
};
export function lipsum(length: number): string {
const words = [
"lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit",
"sed",
"do",
"eiusmod",
"tempor",
"incididunt",
"ut",
"labore",
"et",
"dolore",
"magna",
"aliqua",
'lorem',
'ipsum',
'dolor',
'sit',
'amet',
'consectetur',
'adipiscing',
'elit',
'sed',
'do',
'eiusmod',
'tempor',
'incididunt',
'ut',
'labore',
'et',
'dolore',
'magna',
'aliqua'
];
const result = ["lorem", "ipsum"];
const result = ['lorem', 'ipsum'];
for (let i = 0; i < length; i++) {
result.push(words[Math.floor(Math.random() * words.length)]);
}
return result.join(" ");
return result.join(' ');
}

View File

@ -1,12 +1,12 @@
import i18next from "i18next";
import { createI18nStore } from "svelte-i18next";
import en from "./en.json";
import i18next from 'i18next';
import { createI18nStore } from 'svelte-i18next';
import en from './en.json';
i18next.init({
lng: "en",
lng: 'en',
resources: {
en,
},
en
}
});
export const i18n = createI18nStore(i18next);

View File

@ -1,10 +1,10 @@
import mitt from "mitt";
import mitt from 'mitt';
type NotifyEvents = {
notification: UpNotification;
};
export type UpNotificationLevel = "info" | "warning" | "error";
export type UpNotificationLevel = 'info' | 'warning' | 'error';
export interface INotification {
id: string;
@ -20,7 +20,7 @@ export class UpNotification implements INotification {
constructor(content: string, level?: UpNotificationLevel) {
this.id = String(Math.random());
this.content = content;
this.level = level || "info";
this.level = level || 'info';
}
}

View File

@ -1,16 +1,7 @@
@use "colors";
@use "sass:color";
@use 'colors';
@use 'sass:color';
@mixin rebase(
$rebase03,
$rebase02,
$rebase01,
$rebase00,
$rebase0,
$rebase1,
$rebase2,
$rebase3
) {
@mixin rebase($rebase03, $rebase02, $rebase01, $rebase00, $rebase0, $rebase1, $rebase2, $rebase3) {
--foreground: #{$rebase0};
--foreground-lighter: #{$rebase1};
--foreground-lightest: #{$rebase2};

View File

@ -1,5 +1,5 @@
@use "sass:color";
@use "colors";
@use 'sass:color';
@use 'colors';
html {
box-sizing: border-box;

View File

@ -1,9 +1,9 @@
@import "node_modules/@ibm/plex/scss/ibm-plex.scss";
@import 'node_modules/@ibm/plex/scss/ibm-plex.scss';
html {
--default-font: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif;
--monospace-font: "IBM Plex Mono", "Menlo", "DejaVu Sans Mono", "Consolas",
"Inconsolata", "Hack", monospace;
--default-font: 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
--monospace-font: 'IBM Plex Mono', 'Menlo', 'DejaVu Sans Mono', 'Consolas', 'Inconsolata', 'Hack',
monospace;
font-size: 16px;
}

View File

@ -1,7 +1,7 @@
@use "normalize.css/normalize.css";
@use "colors-app";
@use "fonts";
@use "common";
@use 'normalize.css/normalize.css';
@use 'colors-app';
@use 'fonts';
@use 'common';
body {
height: calc(100vh - 2rem);

View File

@ -1,4 +1,4 @@
import type { IValue } from "@upnd/upend/types";
import type { IValue } from '@upnd/upend/types';
export type WidgetChange =
| AttributeCreate
@ -8,28 +8,28 @@ export type WidgetChange =
| EntryInDelete;
export interface AttributeCreate {
type: "create";
type: 'create';
attribute: string;
value: IValue;
}
export interface AttributeUpdate {
type: "update";
type: 'update';
attribute: string;
value: IValue;
}
export interface AttributeDelete {
type: "delete";
type: 'delete';
address: string;
}
export interface EntryInAdd {
type: "entry-add";
type: 'entry-add';
address: string;
}
export interface EntryInDelete {
type: "entry-delete";
type: 'entry-delete';
address: string;
}

View File

@ -1,4 +1,4 @@
import type { Readable } from "svelte/store";
import type { Readable } from 'svelte/store';
export interface BrowseContext {
index: Readable<number>;

View File

@ -1,28 +1,24 @@
import type { EntityInfo } from "@upnd/upend/types";
import type { UpObject } from "@upnd/upend";
import { ATTR_IN } from "@upnd/upend/constants";
import type { EntityInfo } from '@upnd/upend/types';
import type { UpObject } from '@upnd/upend';
import { ATTR_IN } from '@upnd/upend/constants';
export function getTypes(entity: UpObject, entityInfo: EntityInfo) {
const mimeType = String(entity?.get("FILE_MIME"));
const mimeType = String(entity?.get('FILE_MIME'));
const video =
["video", "application/x-matroska"].some((p) => mimeType.startsWith(p)) ||
entity?.identify().some((l) => l.endsWith(".avi"));
['video', 'application/x-matroska'].some((p) => mimeType.startsWith(p)) ||
entity?.identify().some((l) => l.endsWith('.avi'));
const audio =
(["audio", "application/x-riff"].some((p) => mimeType.startsWith(p)) &&
!video) ||
[".ogg", ".mp3", ".wav"].some(
(suffix) =>
entity?.identify().some((l) => l.toLowerCase().endsWith(suffix)),
(['audio', 'application/x-riff'].some((p) => mimeType.startsWith(p)) && !video) ||
['.ogg', '.mp3', '.wav'].some((suffix) =>
entity?.identify().some((l) => l.toLowerCase().endsWith(suffix))
);
const image = mimeType.startsWith("image");
const text = mimeType.startsWith("text");
const pdf = mimeType.startsWith("application/pdf");
const model =
mimeType?.startsWith("model") ||
entity?.identify().some((l) => l.endsWith(".stl"));
const web = entityInfo?.t == "Url";
const fragment = Boolean(entity?.get("ANNOTATES"));
const image = mimeType.startsWith('image');
const text = mimeType.startsWith('text');
const pdf = mimeType.startsWith('application/pdf');
const model = 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);
@ -36,6 +32,6 @@ export function getTypes(entity: UpObject, entityInfo: EntityInfo) {
model,
web,
fragment,
group,
group
};
}

View File

@ -1,5 +1,5 @@
export function updateTitle(route?: string, title?: string) {
let newTitle = "UpEnd";
let newTitle = 'UpEnd';
if (route?.length) {
newTitle += ` | ${route}`;
}

View File

@ -1,7 +1,7 @@
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { viteStaticCopy } from "vite-plugin-static-copy";
import * as path from "path";
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import * as path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
@ -10,21 +10,21 @@ export default defineConfig({
viteStaticCopy({
targets: [
{
src: path.join(__dirname, "node_modules/boxicons", "fonts"),
dest: path.resolve(__dirname, "dist/vendor/boxicons"),
src: path.join(__dirname, 'node_modules/boxicons', 'fonts'),
dest: path.resolve(__dirname, 'dist/vendor/boxicons')
},
{
src: path.join(__dirname, "node_modules/boxicons", "css"),
dest: path.resolve(__dirname, "dist/vendor/boxicons"),
},
],
}),
src: path.join(__dirname, 'node_modules/boxicons', 'css'),
dest: path.resolve(__dirname, 'dist/vendor/boxicons')
}
]
})
],
server: {
proxy: {
"/api": {
target: "http://localhost:8093/",
},
},
},
'/api': {
target: 'http://localhost:8093/'
}
}
}
});