Initial Commit

This commit is contained in:
2022-12-20 11:54:09 -08:00
commit cea2117b56
33 changed files with 9085 additions and 0 deletions

9
src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}

12
src/app.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

35
src/app.scss Normal file
View File

@@ -0,0 +1,35 @@
/* Write your global styles here, in SCSS syntax. Variables and mixins from the src/variables.scss file are available here without importing */ /* Import only what you need from Bulma */
@import 'bulma/sass/utilities/_all';
@import 'bulma/sass/base/_all';
@import 'bulma/sass/elements/_all';
@import 'bulma/sass/form/_all';
@import 'bulma/sass/components/_all';
@import 'bulma/sass/grid/_all';
@import 'bulma/sass/helpers/_all';
@import 'bulma/sass/layout/_all';
@import './darkly/overrides.scss';
.input::placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
.input::-webkit-input-placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
.input::-moz-placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
.input::-ms-input-placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
pre {
background-color: transparent;
}

291
src/darkly/overrides.scss Normal file
View File

@@ -0,0 +1,291 @@
////////////////////////////////////////////////
// DARKLY
////////////////////////////////////////////////
// Overrides
hr {
height: $border-width;
}
h6 {
text-transform: uppercase;
letter-spacing: 0.5px;
}
.hero {
background-color: $grey-dark;
}
a {
transition: all 200ms ease;
}
.button {
transition: all 200ms ease;
border-width: $border-width;
color: $white;
&.is-active,
&.is-focused,
&:active,
&:focus {
box-shadow: 0 0 0 2px rgba($button-focus-border-color, 0.5);
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
&.is-hovered,
&:hover {
background-color: lighten($color, 7.5%);
}
&.is-active,
&.is-focused,
&:active,
&:focus {
border-color: $color;
box-shadow: 0 0 0 2px rgba($color, 0.5);
}
}
}
}
.label {
color: $grey-lighter;
}
.input,
.textarea {
transition: all 200ms ease;
box-shadow: none;
border-width: $border-width;
padding-left: 1em;
padding-right: 1em;
background-color: $body-background-color;
color: $text
}
.select {
&:after,
select {
border-width: $border-width;
background-color: $body-background-color;
color: $text
}
}
.control {
&.has-addons {
.button,
.input,
.select {
margin-right: -$border-width;
}
}
}
.notification {
background-color: $grey-dark;
}
.card {
$card-border-color: lighten($grey-darker, 5);
box-shadow: none;
border: $border-width solid $card-border-color;
background-color: $grey-darker;
border-radius: $radius;
.card-image {
img {
border-radius: $radius $radius 0 0;
}
}
.card-header {
box-shadow: none;
background-color: rgba($black-bis, 0.2);
border-radius: $radius $radius 0 0;
}
.card-footer {
background-color: rgba($black-bis, 0.2);
}
.card-footer,
.card-footer-item {
border-width: $border-width;
border-color: $card-border-color;
}
}
.notification {
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
a:not(.button) {
color: $color-invert;
text-decoration: underline;
}
}
}
}
.tag {
border-radius: $radius;
}
.menu-list {
a {
transition: all 300ms ease;
}
}
.modal-card-body {
background-color: $grey-darker;
}
.modal-card-foot,
.modal-card-head {
border-color: $grey-dark;
}
.message-header {
font-weight: $weight-bold;
background-color: $grey-dark;
color: $white;
}
.message-body {
border-width: $border-width;
border-color: $grey-dark;
}
.navbar {
border-radius: $radius;
&.is-transparent {
background: none;
}
&.is-primary {
.navbar-dropdown {
a.navbar-item.is-active {
background-color: $link;
}
}
}
@include until($navbar-breakpoint) {
.navbar-menu {
background-color: $navbar-background-color;
border-radius: 0 0 $radius $radius;
}
}
}
.hero .navbar,
body > .navbar {
border-radius: 0;
}
.pagination-link,
.pagination-next,
.pagination-previous {
border-width: $border-width;
}
.panel-block,
.panel-heading,
.panel-tabs {
border-width: $border-width;
&:first-child {
border-top-width: $border-width;
}
}
.panel-heading {
font-weight: $weight-bold;
}
.panel-tabs {
a {
border-width: $border-width;
margin-bottom: -$border-width;
&.is-active {
border-bottom-color: $link-active;
}
}
}
.panel-block {
&:hover {
color: $link-hover;
.panel-icon {
color: $link-hover;
}
}
&.is-active {
.panel-icon {
color: $link-active;
}
}
}
.tabs {
a {
border-bottom-width: $border-width;
margin-bottom: -$border-width;
}
ul {
border-bottom-width: $border-width;
}
&.is-boxed {
a {
border-width: $border-width;
}
li.is-active a {
background-color: darken($grey-darker, 4);
}
}
&.is-toggle {
li a {
border-width: $border-width;
margin-bottom: 0;
}
li + li {
margin-left: -$border-width;
}
}
}
.hero {
// Colors
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
.navbar {
.navbar-dropdown {
.navbar-item:hover {
background-color: $navbar-dropdown-item-hover-background-color;
}
}
}
}
}
}

121
src/darkly/variables.scss Normal file
View File

@@ -0,0 +1,121 @@
////////////////////////////////////////////////
// DARKLY
////////////////////////////////////////////////
// Variables
$grey-lighter: #dbdee0;
$grey-light: #8c9b9d;
$grey: darken($grey-light, 18);
$grey-dark: darken($grey, 18);
$grey-darker: darken($grey, 23);
$white: #FFF;
$orange: #e67e22;
$yellow: #f1b70e;
$green: #2ecc71;
$turquoise: #1abc9c;
$blue: #3498db;
$purple: #8e44ad;
$red: #e74c3c;
$white-ter: #ecf0f1;
$primary: #375a7f !default;
$yellow-invert: #fff;
$family-sans-serif: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI",
"Helvetica Neue", "Helvetica", "Arial", sans-serif;
$family-monospace: "Inconsolata", "Consolas", "Monaco", monospace;
$radius-small: 3px;
$radius: 0.4em;
$radius-large: 8px;
$size-6: 15px;
$size-7: 0.85em;
$title-weight: 500;
$subtitle-weight: 400;
$subtitle-color: $grey-dark;
$border-width: 2px;
$border: $grey;
$body-background-color: darken($grey-darker, 4);
$body-size: 15px;
$background: $grey-darker;
$footer-background-color: $background;
$button-background-color: $background;
$button-border-color: lighten($button-background-color, 15);
$title-color: #fff;
$subtitle-color: $grey-light;
$subtitle-strong-color: $grey-light;
$text: #fff;
$text-light: lighten($text, 10);
$text-strong: darken($text, 5);
$box-color: $text;
$box-background-color: $grey-dark;
$box-shadow: none;
$link: $turquoise;
$link-hover: lighten($link, 5);
$link-focus: darken($link, 5);
$link-active: darken($link, 5);
$link-focus-border: $grey-light;
$button-color: $primary;
$button-hover-color: darken($text, 5); // text-dark
$button-focus: darken($text, 5); // text-dark
$button-active-color: darken($text, 5); // text-dark
$button-disabled-background-color: $grey-light;
$button-focus-border-color: $link-focus-border;
$control-height: 2.5em;
$input-color: $grey-darker;
$input-icon-color: $grey;
$input-icon-active-color: $input-color;
$input-hover-color: $grey-light;
$input-disabled-background-color: $grey-light;
$input-disabled-border: $grey-lighter;
$table-color: $text;
$table-head: $grey-lighter;
$table-background-color: $grey-dark;
$table-cell-border: 1px solid $grey;
$table-row-hover-background-color: $grey-darker;
$table-striped-row-even-background-color: $grey-darker;
$table-striped-row-even-hover-background-color: lighten($grey-darker, 2);
$pagination-color: $link;
$pagination-border-color: $border;
$navbar-height: 4rem;
$navbar-background-color: $primary;
$navbar-item-color: $text;
$navbar-item-hover-color: $link;
$navbar-item-hover-background-color: transparent;
$navbar-item-active-color: $link;
$navbar-dropdown-arrow: #fff;
$navbar-divider-background-color: rgba(0, 0, 0, 0.2);
$navbar-dropdown-border-top: 1px solid $navbar-divider-background-color;
$navbar-dropdown-background-color: $primary;
$navbar-dropdown-item-hover-color: $grey-lighter;
$navbar-dropdown-item-hover-background-color: transparent;
$navbar-dropdown-item-active-background-color: transparent;
$navbar-dropdown-item-active-color: $link;
$dropdown-content-background-color: $background;
$dropdown-item-color: $text;
$progress-value-background-color: $grey-lighter;
$bulmaswatch-import-font: true !default;
$file-cta-background-color: $grey-darker;
$progress-bar-background-color: $grey-dark;
$panel-heading-background-color: $grey-dark;

1830
src/lib/lure.pb.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
<script>
import '../app.scss';
</script>
<slot />

101
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,101 @@
<script>
import { onMount } from "svelte";
import Header from "./header.svelte";
import Highlight from 'svelte-highlight';
import bash from 'svelte-highlight/languages/bash';
import agate from 'svelte-highlight/styles/agate';
let Carousel;
onMount(async () => {
// Perform dynamic import of svelte-carousel because it breaks
// if imported during SSR
Carousel = (await import('svelte-carousel')).default;
})
let images = [
{
path: "/lure-arch.gif",
caption: "Recorded on Arch Linux with LURE commit 81013ce",
alt: "GIF depicting an Arch Linux terminal with LURE installing a package"
},
{
path: "/lure-debian.gif",
caption: "Recorded on Debian Sid with LURE commit 81013ce",
alt: "GIF depicting a Debian terminal with LURE installing a package"
},
{
path: "/lure-fedora.gif",
caption: "Recorded on Fedora 36 with LURE commit 81013ce",
alt: "GIF depicting a Fedora terminal with LURE installing a package"
},
{
path: "/lure-alpine.gif",
caption: "Recorded on Alpine Linux 3.16 with commit 81013ce",
alt: "GIF depicting an Alpine Linux terminal with LURE installing a package"
},
];
</script>
<svelte:head>
{@html agate}
</svelte:head>
<Header/>
<section class="container">
<p class="title">LURE</p>
<p class="subtitle">The AUR missing from most Linux distributions</p>
<hr>
<div class="columns">
<div class="column">
<div class="card">
<div class="card-header">
<p class="card-header-title">Why should I use it?</p>
</div>
<div class="card-content">
LURE allows you to install your favorite software that may not be
popular enough to be placed into your distro's repos, while preserving
all the benefits of installing from repos, such as updates, easy
uninstalling, etc. It also allows developers to provide a single place
for their users to install their software.
</div>
</div>
</div>
<div class="column">
<div class="card">
<div class="card-header">
<p class="card-header-title">How does it work?</p>
</div>
<div class="card-content">
LURE works by abstracting package formats and package managers. It
builds native packages for your native package manager using AUR
PKGBUILD-like bash scripts, then installs them using that package manager.
This means that once LURE has successfully installed a package, it acts
just like any other package and can be managed without LURE.
</div>
</div>
</div>
</div>
<p class="title">Installation</p>
<p>LURE can easily be installed by running its install script:</p>
<Highlight language={bash} code='curl https://www.arsenm.dev/lure.sh | bash'/>
<p>
It's also available on the AUR as <a href="https://aur.archlinux.org/packages/lure-bin"><code>lure-bin</code></a> and distro
packages are provided at the <a href="https://gitea.arsenm.dev/Arsen6331/lure/releases/latest">latest Gitea release</a>.
</p>
<br>
<p class="title">Examples</p>
<!-- Use dynamically imported Carousel module -->
<svelte:component this={Carousel}>
{#each images as image}
<figure class="has-text-centered">
<img src={image.path} alt={image.alt ?? "GIF depicting LURE installing a package"}>
<figcaption>{image.caption ?? ""}</figcaption>
</figure>
{/each}
</svelte:component>
</section>

View File

@@ -0,0 +1,52 @@
<script>
import Header from "../header.svelte";
</script>
<Header/>
<div class="container">
<p class="title">FAQ</p>
<br>
<p class="subtitle" id='distro-support'>Why isn't my distro supported?</p>
<p>
In order to support a distro, LURE has to be able to talk to its package manager
as well as build packages for it. Talking to the package manager is relatively simple,
LURE just needs to be told which commands to run, but the package formats are much more
complex. LURE uses <a href="https://github.com/goreleaser/nfpm">nFPM</a> to handle
package formats, and nFPM supports only <code>deb</code>, <code>rpm</code>, <code>apk</code>,
and <code>archlinux</code> package formats. When I started LURE, nFPM didn't have support for
archlinux packages, so I added it in a PR. Despite Arch having a very simple package format, it took
over 1k lines of code to implement, so supporting a distro with a different format, like
<code>xbps</code> for Void Linux, is very complicated, and may take some time.
</p>
<hr>
<p class="subtitle" id='flatpak-snap-appimage'>Why use LURE instead of Flatpak, Snap, or AppImage?</p>
<p>
LURE is not meant to solve the same problems as Flatpak, Snap, and AppImage. These are containerized
package formats that allow the creation of a single package that works on all distros. This cross-platform package
contains the program as well as everything else needed for that program to run. It works using
something called a container, which can create issues where programs are slow to start, don't follow
your system's settings, or can't access certain parts of your system. If you want most programs to work every
time, or you're running an old distro with old packages, you should use containzerized formats.
LURE, on the other hand, does not use containers. It builds the program from source automatically and
installs it. It also doesn't have its own package format. Instead, it uses the same one as the distro
it's running on, making LURE packages act the same way as your distro's packages. This means that
unlike Snap and Flatpak, LURE is not a package manager. It simply uses your distro's package manager,
which allows you to manage the packages installed by LURE even without it installed.
</p>
<br>
<p>
However, LURE also has some downsides. Because of the fact that it builds programs automatically from source,
certain packages, especially git packages that retrieve the latest code from git, may not work on older distros,
or distros like Debian which have old packages. Please consider your use case and whether such downsides are
acceptable before using LURE. Also note that, like the AUR, all packages are user-submitted and are not vetted,
so while unlikely, they may contain malicious code. It is the responsibility of the user to read the build script
and ensure this is not the case. Malicious packages should be reported by opening an issue on the git repo containing them.
</p>
<hr>
<p class="subtitle" id='adding-packages'>How do I add my own package to LURE?</p>
<p>
Take a look at the documentation for <a href="https://github.com/Arsen6331/lure/blob/master/docs/build-scripts.md">Build Scripts</a>
and <a href="https://github.com/Arsen6331/lure/blob/master/docs/adding-packages.md">Adding Packages to LURE's repo</a>.
</p>
</div>

30
src/routes/header.svelte Normal file
View File

@@ -0,0 +1,30 @@
<script>
import { page } from '$app/stores';
import Icon from '@iconify/svelte';
function isActive(path) {
return $page.route.id == path ? 'is-active' : ''
}
let navbarIsActive = false;
</script>
<nav class="navbar mb-5">
<div class="navbar-brand">
<a class="navbar-item" href="/">LURE Web</a>
<button class="navbar-burger {navbarIsActive ? 'is-active' : ''}" aria-label="menu" on:click={() => {navbarIsActive = !navbarIsActive}}>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</button>
</div>
<div id="main-navbar" class="navbar-menu {navbarIsActive ? 'is-active' : ''}">
<div class="navbar-end">
<a class="navbar-item {isActive('/')}" href="/"><Icon icon="material-symbols:house" inline=true/> Home</a>
<a class="navbar-item {isActive('/pkgs')}" href="/pkgs"><Icon icon="mdi:package-variant-closed" inline=true/>&nbsp;Packages</a>
<a class="navbar-item {isActive('/faq')}" href="/faq"><Icon icon="mdi:question-mark-circle" inline=true/>&nbsp;FAQ</a>
</div>
</div>
</nav>

View File

@@ -0,0 +1,111 @@
<script>
import { page } from '$app/stores';
import { client } from 'twirpscript';
import { GetPkg } from '$lib/lure.pb';
import { LURE_WEB_API_URL } from '$env/static/public';
import { DoubleBounce } from 'svelte-loading-spinners';
import Header from "../../../header.svelte";
import Icon from '@iconify/svelte';
client.baseURL = LURE_WEB_API_URL
client.prefix = ""
function fullVer(pkg) {
let ver = `${pkg.version}-${pkg.release}`;
if (pkg.epoch != 0) {
ver = `${pkg.epoch}:${ver}`
}
return ver
}
function objToMap(o) {
return new Map(Object.entries(o))
}
</script>
<Header/>
<section class="container">
<a href="/pkgs"><Icon icon="material-symbols:arrow-back-rounded" inline=true/> Back</a>
{#await GetPkg({name: $page.params.name, repository: $page.params.repo})}
<center><DoubleBounce color="#375a7f" /></center>
{:then pkg}
<p class="title">{pkg.name}</p>
<p class="subtitle mb-3">{fullVer(pkg)}</p>
<a href="/pkg/{pkg.repository}/{pkg.name}/lure.sh" class="button is-primary mb-5">View lure.sh</a>
<div class="box">
<table class="table is-hoverable is-fullwidth">
<tbody>
{#if pkg.description != ""}
<tr>
<th>Description:</th>
<td>{pkg.description}</td>
</tr>
{/if}
{#if pkg.homepage != ""}
<tr>
<th>Homepage:</th>
<td><a href="{pkg.homepage}">{pkg.homepage}</a></td>
</tr>
{/if}
{#if pkg.maintainer != ""}
<tr>
<th>Maintainer:</th>
<td>{pkg.maintainer}</td>
</tr>
{/if}
{#if pkg.licenses.length != 0}
<tr>
<th>Licenses:</th>
<td>
{#each pkg.licenses as license, index}
<a href="https://spdx.org/licenses/{license}.html">{license}</a>{#if index+1 < pkg.licenses.length},&nbsp;{/if}
{/each}
</td>
</tr>
{/if}
{#if pkg.architectures.length != 0}
<tr>
<th>Architectures:</th>
<td>{pkg.architectures.join(', ')}</td>
</tr>
{/if}
{#if pkg.conflicts.length != 0}
<tr>
<th>Conflicts:</th>
<td>{pkg.conflicts.join(', ')}</td>
</tr>
{/if}
{#if pkg.provides.length != 0}
<tr>
<th>Provides:</th>
<td>{pkg.provides.join(', ')}</td>
</tr>
{/if}
{#each [...objToMap(pkg.depends)] as [override, pkgList]}
<tr>
<th>Depends ({override == "" ? "default" : override}):</th>
<td>{pkgList.entries.join(', ')}</td>
</tr>
{/each}
{#each [...objToMap(pkg.buildDepends)] as [override, pkgList]}
<tr>
<th>Build Depends ({override != "" ? override : "default"}):</th>
<td>{pkgList.entries.join(', ')}</td>
</tr>
{/each}
<tr>
<th>Repository:</th>
<td>{pkg.repository}</td>
</tr>
</tbody>
</table>
</div>
{:catch err}
<div class="notification is-danger my-3">
Error: {err.msg}
</div>
{/await}
</section>

View File

@@ -0,0 +1,42 @@
<script>
import { page } from '$app/stores';
import { client } from 'twirpscript';
import { GetBuildScript } from '$lib/lure.pb';
import { LURE_WEB_API_URL } from '$env/static/public';
import { DoubleBounce } from 'svelte-loading-spinners';
import Highlight from 'svelte-highlight';
import bash from 'svelte-highlight/languages/bash';
import agate from 'svelte-highlight/styles/agate';
import Header from "../../../../header.svelte";
import Icon from '@iconify/svelte';
client.baseURL = LURE_WEB_API_URL
client.prefix = ""
</script>
<svelte:head>
{@html agate}
</svelte:head>
<Header/>
<section class="container">
<a href="."><Icon icon="material-symbols:arrow-back-rounded" inline=true/> Back</a>
{#await GetBuildScript({name: $page.params.name, repository: $page.params.repo})}
<center><DoubleBounce color="#375a7f" /></center>
{:then resp}
<p class="title">Build Script</p>
<p class="subtitle">{$page.params.repo} / {$page.params.name}</p>
<div class="box">
<Highlight language={bash} code={resp.script}/>
</div>
{:catch err}
<div class="notification is-danger my-3">
Error: {err.msg}
</div>
{/await}
</section>

View File

@@ -0,0 +1,121 @@
<script>
import { client } from 'twirpscript';
import { Search } from '$lib/lure.pb';
import { searchReq } from '../../stores.js';
import { LURE_WEB_API_URL } from '$env/static/public';
import Header from "../header.svelte";
import { DoubleBounce } from 'svelte-loading-spinners';
import Icon from '@iconify/svelte';
client.baseURL = LURE_WEB_API_URL
client.prefix = ""
let searchPromise = Search(
$searchReq ?? {
query: "",
limit: BigInt(0),
sortBy: "UNSORTED",
filterType: "NO_FILTER",
}
);
function onSubmit(e) {
let formData = new FormData(e.target);
let filterValue = formData.get('filter-value');
let req = {
query: formData.get('query'),
limit: BigInt(0),
sortBy: formData.get('sort-by'),
filterType: formData.get('filter'),
filterValue: filterValue == '' ? null : filterValue,
}
searchReq.set(req)
searchPromise = Search(req);
}
function onFilterChange(e) {
let filterVal = document.getElementById('filter-val');
if (e.target.value != "NO_FILTER") {
filterVal.classList.remove('is-hidden');
} else {
filterVal.classList.add('is-hidden');
}
}
</script>
<Header/>
<section class="container">
<p class="title">Package Search</p>
<form on:submit|preventDefault={onSubmit}>
<div class="field">
<div class="control">
<input class="input" type="text" name="query" value="" placeholder="Query">
</div>
</div>
<div class="columns">
<div class="column">
<div class="field">
<div class="control select is-fullwidth">
<select name="sort-by">
<option value="UNSORTED" selected>Unsorted</option>
<option value="NAME">Sort by Name</option>
<option value="VERSION">Sort by Version</option>
<option value="REPOSITORY">Sort by Repository</option>
</select>
</div>
</div>
</div>
<div class="column">
<div class="field">
<div class="control select is-fullwidth">
<select name="filter" on:change={onFilterChange}>
<option value="NO_FILTER" selected>No Filter</option>
<option value="IN_REPOSITORY">In Repo</option>
<option value="SUPPORTS_ARCH">Supports Architecture</option>
</select>
</div>
</div>
</div>
</div>
<div class="field">
<div class="control">
<input id="filter-val" class="input is-hidden" type="text" value="" name="filter-value" placeholder="Filter Value">
</div>
</div>
<input type="submit" class="button is-fullwidth" value="Search">
</form>
<hr>
{#await searchPromise}
<center><DoubleBounce color="#375a7f" /></center>
{:then resp}
{#each resp.packages as pkg}
<div class="card my-5">
<header class="card-header">
<p class="card-header-title">{pkg.repository} / {pkg.name}</p>
<div class="card-header-icon">{pkg.version}</div>
</header>
<div class="card-content">
{pkg.description}
</div>
<footer class="card-footer">
<a class="card-footer-item" href="/pkg/{pkg.repository}/{pkg.name}">More info <Icon icon="material-symbols:arrow-forward-rounded" inline=true/></a>
</footer>
</div>
{/each}
{#if resp.packages.length === 0}
<p class="subtitle has-text-centered has-text-danger my-5 is-fullwidth">No Results</p>
{/if}
{:catch err}
<div class="notification is-danger my-3">
Error: {err.msg}
</div>
{/await}
</section>

3
src/stores.js Normal file
View File

@@ -0,0 +1,3 @@
import { writable } from "svelte/store";
export const searchReq = writable()

6
src/variables.scss Normal file
View File

@@ -0,0 +1,6 @@
/* Variables and mixins declared here will be available in all other SCSS files */ /* https://github.com/jgthms/bulma/issues/1293 */
$body-overflow-y: auto;
@import './darkly/variables.scss';
$family-sans-serif: "Ubuntu", sans-serif;