mirror of
https://github.com/ivabus/gui
synced 2025-04-23 14:07:14 +03:00
add-confetti (#652)
This commit is contained in:
parent
f9031a59df
commit
543411433b
17 changed files with 232 additions and 104 deletions
|
@ -33,7 +33,8 @@ export async function loadPackageCache(): Promise<Packages> {
|
|||
if (pkgs?.packages) {
|
||||
// Remove any temporary properties that may have been added to the package (like installation progress)
|
||||
for (const [key, value] of Object.entries(pkgs.packages)) {
|
||||
const { install_progress_percentage, isUninstalling, synced, ...rest } = value;
|
||||
const { install_progress_percentage, isUninstalling, synced, displayState, ...rest } =
|
||||
value;
|
||||
pkgs.packages[key] = rest;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"@testing-library/svelte": "^3.2.2",
|
||||
"@testing-library/webdriverio": "^3.2.1",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/canvas-confetti": "^1.6.0",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/mixpanel-browser": "^2.38.1",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
|
@ -94,6 +95,7 @@
|
|||
"axios": "^1.3.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"buffer": "^6.0.3",
|
||||
"canvas-confetti": "^1.6.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"custom-electron-titlebar": "4.2.0-beta.0",
|
||||
"dayjs": "^1.11.7",
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<script lang="ts">
|
||||
import type { GUIPackage } from "$libs/types";
|
||||
import confetti from "canvas-confetti";
|
||||
import { afterUpdate } from "svelte";
|
||||
import { packagesStore } from "$libs/stores";
|
||||
import Button from "@tea/ui/button/button.svelte";
|
||||
|
||||
let root: HTMLElement | undefined;
|
||||
|
||||
export let pkg: GUIPackage;
|
||||
|
||||
$: isAnimating = false;
|
||||
|
||||
const playConfetti = async () => {
|
||||
if (isAnimating) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (root) {
|
||||
isAnimating = true;
|
||||
packagesStore.resetPackageDisplayState(pkg);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.style.width = "100%";
|
||||
canvas.style.height = "100%";
|
||||
root.appendChild(canvas);
|
||||
|
||||
const myConfetti = confetti.create(canvas, { resize: true });
|
||||
await myConfetti({ particleCount: 500, spread: 360, startVelocity: 20, gravity: 0.5 });
|
||||
|
||||
root.removeChild(canvas);
|
||||
isAnimating = false;
|
||||
}
|
||||
};
|
||||
|
||||
const retry = () => {
|
||||
packagesStore.resetPackageDisplayState(pkg);
|
||||
packagesStore.installPkg(pkg, pkg.displayState?.version);
|
||||
};
|
||||
|
||||
afterUpdate(() => {
|
||||
if (pkg.displayState?.kind === "INSTALLED_SUCCESSFULLY") {
|
||||
playConfetti();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={root} class="z-60 pointer-events-none absolute left-0 top-0 h-full w-full">
|
||||
{#if pkg.displayState?.kind === "INSTALLATION_ERROR"}
|
||||
<div
|
||||
class="pointer-events-auto flex h-full w-full flex-col items-center justify-center bg-black/90"
|
||||
>
|
||||
<div><i class="icon-exlamation-outline text-3xl text-[#FF4100]" /></div>
|
||||
<div class="mb-2 font-mona">Install Failed</div>
|
||||
<div class="mb-2 w-1/2">
|
||||
<Button type="plain" color="blue" class="p-2 text-xs" onClick={retry}>RETRY</Button>
|
||||
</div>
|
||||
<button class="text-xs text-gray" on:click={() => packagesStore.resetPackageDisplayState(pkg)}
|
||||
>cancel</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
|
@ -16,6 +16,7 @@
|
|||
import PackageVersionSelector from "$components/package-install-button/package-version-selector.svelte";
|
||||
import { fixPackageName } from "$libs/packages/pkg-utils";
|
||||
import { semverCompare } from "$libs/packages/pkg-utils";
|
||||
import InstallResultOverlay from "$components/install-result-overlay/install-result-overlay.svelte";
|
||||
|
||||
export let pkg: GUIPackage;
|
||||
let installing = false;
|
||||
|
@ -88,6 +89,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<InstallResultOverlay {pkg} />
|
||||
</figure>
|
||||
<article class="w-2/3 p-4 pt-8">
|
||||
<div class="align-center flex items-center gap-2">
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import PackageInstallButton from "$components/package-install-button/package-install-button.svelte";
|
||||
import PackageInstalledBadge from "$components/package-install-button/package-installed-badge.svelte";
|
||||
import { fixPackageName } from "$libs/packages/pkg-utils";
|
||||
import InstallResultOverlay from "$components/install-result-overlay/install-result-overlay.svelte";
|
||||
|
||||
export let pkg: GUIPackage;
|
||||
export let link: string;
|
||||
|
@ -26,74 +27,77 @@
|
|||
const preventPropagation = (evt: MouseEvent) => evt.stopPropagation();
|
||||
</script>
|
||||
|
||||
<section class="package-card relative h-auto border border-gray {layout}" class:active={isActive}>
|
||||
<BgImage class="absolute left-0 top-0 h-full w-full" {layout} {pkg} />
|
||||
<div class="relative">
|
||||
<section class="package-card relative h-auto border border-gray {layout}" class:active={isActive}>
|
||||
<BgImage class="absolute left-0 top-0 h-full w-full" {layout} {pkg} />
|
||||
|
||||
<a href={link} on:mousedown={activate} on:mouseup={deactivate} on:mouseleave={deactivate}>
|
||||
<div
|
||||
data-testid={`package-card-${pkg.slug}`}
|
||||
class="package-card-content absolute h-full w-full flex-col justify-between"
|
||||
>
|
||||
<div class="hint-container">
|
||||
<div class="hint">
|
||||
<div class="text-xs line-clamp-1">view more details</div>
|
||||
<div class="hint-icon"><i class="icon-upward-arrow" /></div>
|
||||
<a href={link} on:mousedown={activate} on:mouseup={deactivate} on:mouseleave={deactivate}>
|
||||
<div
|
||||
data-testid={`package-card-${pkg.slug}`}
|
||||
class="package-card-content absolute h-full w-full flex-col justify-between"
|
||||
>
|
||||
<div class="hint-container">
|
||||
<div class="hint">
|
||||
<div class="text-xs line-clamp-1">view more details</div>
|
||||
<div class="hint-icon"><i class="icon-upward-arrow" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-container absolute bottom-0 w-full {layout}">
|
||||
<article class="card-thumb-label relative">
|
||||
{#if layout === "bottom"}
|
||||
<h3 class="text-bold font-mona text-2xl font-bold text-white line-clamp-1">
|
||||
{fixPackageName(pkg.name)}
|
||||
</h3>
|
||||
<p class="h-[32px] text-xs font-thin lowercase line-clamp-2">{pkg.desc ?? ""}</p>
|
||||
{:else}
|
||||
<h3 class="text-bold mb-4 font-mona text-3xl font-bold text-white line-clamp-1">
|
||||
{fixPackageName(pkg.name)}
|
||||
</h3>
|
||||
<p class="line-clamp-[8] h-[160px] text-[14px] font-thin lowercase leading-[20px]">
|
||||
{pkg.desc ?? ""}
|
||||
</p>
|
||||
{/if}
|
||||
</article>
|
||||
<div class="mt-3.5 w-full">
|
||||
<div class="flex w-fit flex-col items-center">
|
||||
<div class="install-button {layout}" on:mousedown={preventPropagation}>
|
||||
{#if pkg.state === PackageStates.INSTALLED}
|
||||
<PackageInstalledBadge {pkg} />
|
||||
{:else}
|
||||
<PackageInstallButton
|
||||
{pkg}
|
||||
onClick={(evt) => {
|
||||
// prevent default to prevent the link that this button is inside of from being followed
|
||||
evt?.preventDefault();
|
||||
onClickCTA();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mt-1.5 h-[10px] leading-[10px]">
|
||||
{#if pkg.state === "NEEDS_UPDATE"}
|
||||
<span class="text-[10px]">
|
||||
<span class="opacity-70">you have</span>
|
||||
v{findRecentInstalledVersion(pkg)}
|
||||
</span>
|
||||
{/if}
|
||||
<div class="content-container absolute bottom-0 w-full {layout}">
|
||||
<article class="card-thumb-label relative">
|
||||
{#if layout === "bottom"}
|
||||
<h3 class="text-bold font-mona text-2xl font-bold text-white line-clamp-1">
|
||||
{fixPackageName(pkg.name)}
|
||||
</h3>
|
||||
<p class="h-[32px] text-xs font-thin lowercase line-clamp-2">{pkg.desc ?? ""}</p>
|
||||
{:else}
|
||||
<h3 class="text-bold mb-4 font-mona text-3xl font-bold text-white line-clamp-1">
|
||||
{fixPackageName(pkg.name)}
|
||||
</h3>
|
||||
<p class="line-clamp-[8] h-[160px] text-[14px] font-thin lowercase leading-[20px]">
|
||||
{pkg.desc ?? ""}
|
||||
</p>
|
||||
{/if}
|
||||
</article>
|
||||
<div class="mt-3.5 w-full">
|
||||
<div class="flex w-fit flex-col items-center">
|
||||
<div class="install-button {layout}" on:mousedown={preventPropagation}>
|
||||
{#if pkg.state === PackageStates.INSTALLED}
|
||||
<PackageInstalledBadge {pkg} />
|
||||
{:else}
|
||||
<PackageInstallButton
|
||||
{pkg}
|
||||
onClick={(evt) => {
|
||||
// prevent default to prevent the link that this button is inside of from being followed
|
||||
evt?.preventDefault();
|
||||
onClickCTA();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mt-1.5 h-[10px] leading-[10px]">
|
||||
{#if pkg.state === "NEEDS_UPDATE"}
|
||||
<span class="text-[10px]">
|
||||
<span class="opacity-70">you have</span>
|
||||
v{findRecentInstalledVersion(pkg)}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</a>
|
||||
|
||||
{#if progessLoading > 0 && progessLoading < 100}
|
||||
<div class="absolute left-0 top-0 z-40 h-full w-full bg-black bg-opacity-50">
|
||||
<div class="absolute left-0 right-0 top-1/2 m-auto -mt-12 h-24 w-24">
|
||||
<ProgressCircle value={progessLoading} />
|
||||
{#if progessLoading > 0 && progessLoading < 100}
|
||||
<div class="absolute left-0 top-0 z-40 h-full w-full bg-black bg-opacity-50">
|
||||
<div class="absolute left-0 right-0 top-1/2 m-auto -mt-12 h-24 w-24">
|
||||
<ProgressCircle value={progessLoading} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
</section>
|
||||
<InstallResultOverlay {pkg} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
section {
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
? 'justify-between'
|
||||
: 'justify-center'}"
|
||||
>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="flex items-center gap-x-2" data-testid={`install-badge-${pkg?.slug}`}>
|
||||
<div>{ctaLabel}</div>
|
||||
<div class="version-label {badgeClass[pkg.state]}">v{getVersion(pkg)}</div>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
export let pkg: GUIPackage | null = null;
|
||||
</script>
|
||||
|
||||
<div class="container relative h-full">
|
||||
<div class="container relative h-full" data-testid={`install-badge-${pkg?.slug}`}>
|
||||
<div class="content flex items-center justify-center gap-2 p-2">
|
||||
<i class="icon-check-circle-o flex text-sm text-[#00ffd0]" />
|
||||
<div class="text-xs">INSTALLED</div>
|
||||
|
|
|
@ -226,23 +226,23 @@ const installPkg = async (pkg: GUIPackage, version?: string) => {
|
|||
|
||||
try {
|
||||
updatePackage(pkg.full_name, { install_progress_percentage: 0.01 });
|
||||
|
||||
await installPackage(pkg, versionToInstall);
|
||||
trackInstall(pkg.full_name);
|
||||
|
||||
await refreshSinglePackage(pkg.full_name);
|
||||
|
||||
notificationStore.add({
|
||||
message: `Package ${pkg.full_name} v${versionToInstall} has been installed.`
|
||||
});
|
||||
if (pkg.state === PackageStates.AVAILABLE) {
|
||||
updatePackage(pkg.full_name, {
|
||||
displayState: { kind: "INSTALLED_SUCCESSFULLY", version: versionToInstall }
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
let message = "Unknown Error";
|
||||
if (error instanceof Error) message = error.message;
|
||||
trackInstallFailed(pkg.full_name, message || "unknown");
|
||||
|
||||
notificationStore.add({
|
||||
message: `Package ${pkg.full_name} v${versionToInstall} failed to install.`,
|
||||
type: NotificationType.ERROR
|
||||
updatePackage(pkg.full_name, {
|
||||
displayState: { kind: "INSTALLATION_ERROR", errorMessage: message, version: versionToInstall }
|
||||
});
|
||||
} finally {
|
||||
updatePackage(pkg.full_name, { install_progress_percentage: 100 });
|
||||
|
@ -358,6 +358,12 @@ const setBadgeCountFromPkgs = (pkgs: Packages) => {
|
|||
}
|
||||
};
|
||||
|
||||
const resetPackageDisplayState = (pkg: GUIPackage) => {
|
||||
updatePackage(pkg.full_name, {
|
||||
displayState: null
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
packageList,
|
||||
search: searchPackages,
|
||||
|
@ -367,5 +373,6 @@ export default {
|
|||
syncPackageData,
|
||||
deletePkg,
|
||||
destroy,
|
||||
cachePkgImage
|
||||
cachePkgImage,
|
||||
resetPackageDisplayState
|
||||
};
|
||||
|
|
|
@ -13,6 +13,16 @@ export enum PackageStates {
|
|||
UPDATING = "UPDATING"
|
||||
}
|
||||
|
||||
// PackageDisplayState is for showing temporary UI elements when a package is installed or updated. It is not persisted.
|
||||
export interface PackageDisplayState {
|
||||
// INSTALLED_SUCCESSFULLY -> The pacakge was installed successfully show some confetti!
|
||||
// UPDATED_SUCCESSFULLY -> The package was updated successfully change the badge temporarily
|
||||
// INSTALLATION_ERROR -> The package failed to install show an error overlay temporarily
|
||||
kind: "INSTALLED_SUCCESSFULLY" | "UPDATED_SUCCESSFULLY" | "INSTALLATION_ERROR";
|
||||
errorMessage?: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export type Packages = {
|
||||
version: string;
|
||||
packages: { [full_name: string]: GUIPackage };
|
||||
|
@ -25,6 +35,8 @@ export type GUIPackage = Package & {
|
|||
install_progress_percentage?: number;
|
||||
isUninstalling?: boolean;
|
||||
cached_image_url?: string;
|
||||
|
||||
displayState?: PackageDisplayState | null;
|
||||
};
|
||||
|
||||
export type Course = {
|
||||
|
|
|
@ -14,6 +14,8 @@ describe("basic smoke test", () => {
|
|||
it("install brewkit from the made by tea tab", async () => {
|
||||
const { screen } = utils!;
|
||||
|
||||
const slug = "tea_xyz_brewkit";
|
||||
|
||||
// app launches to discover screen by default - make sure Stable Diffusion is there
|
||||
await expect(await screen.findByText("Stable Diffusion web UI")).toExist();
|
||||
|
||||
|
@ -22,7 +24,7 @@ describe("basic smoke test", () => {
|
|||
btn.click();
|
||||
|
||||
// find the brewkit package
|
||||
const pkgCard = await utils.findPackageCardBySlug("tea_xyz_brewkit");
|
||||
const pkgCard = await utils.findPackageCardBySlug(slug);
|
||||
pkgCard.click();
|
||||
|
||||
await utils.packageDetailsLoaded();
|
||||
|
@ -30,14 +32,14 @@ describe("basic smoke test", () => {
|
|||
// Be nice to devs running this over and over
|
||||
await utils.uninstallPackageIfNeeded();
|
||||
|
||||
await utils.installLatestVersion("tea_xyz_brewkit");
|
||||
await utils.installLatestVersion(slug);
|
||||
|
||||
await utils.verifyAndCloseNotification(/^Package tea.xyz\/brewkit .* has been installed./);
|
||||
await utils.verifyInstalledBadge(slug);
|
||||
await expect(await screen.findByRole("button", { name: "OPEN IN TERMINAL" })).toExist();
|
||||
});
|
||||
|
||||
it("search and install create-dmg", async () => {
|
||||
const { screen, searchTerm } = utils!;
|
||||
const { searchTerm } = utils!;
|
||||
await searchTerm("create-dmg");
|
||||
|
||||
const packageFullname = "github.com/create-dmg/create-dmg";
|
||||
|
@ -49,24 +51,24 @@ describe("basic smoke test", () => {
|
|||
await utils.packageDetailsLoaded();
|
||||
await utils.uninstallPackageIfNeeded();
|
||||
|
||||
await utils.installLatestVersion("github_com_create_dmg_create_dmg");
|
||||
|
||||
await utils.verifyAndCloseNotification(
|
||||
/^Package github.com\/create-dmg\/create-dmg .* has been installed./
|
||||
);
|
||||
await utils.installLatestVersion(createDmgSlug);
|
||||
await utils.verifyInstalledBadge(createDmgSlug);
|
||||
});
|
||||
|
||||
it("should be able to install specific version", async () => {
|
||||
const { screen, searchTerm } = utils!;
|
||||
const slug = "gnu_org_grep";
|
||||
|
||||
await searchTerm("grep");
|
||||
const grepCard = await utils.findSearchPackageCardBySlug("gnu_org_grep");
|
||||
const grepCard = await utils.findSearchPackageCardBySlug(slug);
|
||||
await expect(grepCard).toExist();
|
||||
grepCard.click();
|
||||
|
||||
await utils.uninstallPackageIfNeeded();
|
||||
await utils.installSpecificVersion("gnu_org_grep", "3.8.0");
|
||||
await utils.installSpecificVersion(slug, "3.8.0");
|
||||
|
||||
await utils.verifyAndCloseNotification(/^Package gnu.org\/grep .* has been installed./);
|
||||
// since we're installing an old version verify the badge says UPDATE
|
||||
await utils.verifyInstalledBadge(slug, "UPDATE");
|
||||
|
||||
// Now test the update
|
||||
await utils.goHome();
|
||||
|
@ -81,6 +83,7 @@ describe("basic smoke test", () => {
|
|||
await expect(updateBtn).toExist();
|
||||
updateBtn.click();
|
||||
|
||||
await utils.verifyAndCloseNotification(/^Package gnu.org\/grep .* has been installed./);
|
||||
// FIXME: This should test for the button saying "UPDATED", but that feature is not done yet so uncomment this when it is
|
||||
//await utils.verifyInstalledBadge(slug, "UPDATED");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as testingLibraryWdio from "@testing-library/webdriverio";
|
||||
import { waitForNotExist } from "./waitutils.ts";
|
||||
import { sleep, waitForNotExist } from "./waitutils.ts";
|
||||
|
||||
// work around for importing CommonJS module
|
||||
const { setupBrowser } = testingLibraryWdio;
|
||||
|
@ -13,11 +13,7 @@ export function setupUtils(browser: WebdriverIO.Browser) {
|
|||
};
|
||||
|
||||
const findByTestId = async (testId: string) => {
|
||||
return (await screen.findByTestId(
|
||||
testId,
|
||||
{},
|
||||
{ interval: 1000, timeout: 10000 }
|
||||
)) as unknown as HTMLElement;
|
||||
return await screen.findByTestId(testId, {}, { interval: 1000, timeout: 10000 });
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -93,18 +89,25 @@ export function setupUtils(browser: WebdriverIO.Browser) {
|
|||
await searchInput.setValue(term);
|
||||
};
|
||||
|
||||
const closeNotification = async () => {
|
||||
console.log("Closing notification");
|
||||
const closeNotificationBtn = await findByTestId("close-notification");
|
||||
await expect(closeNotificationBtn).toExist();
|
||||
closeNotificationBtn.click();
|
||||
await waitForNotExist(() => screen.queryByTestId("close-notification"));
|
||||
};
|
||||
const verifyInstalledBadge = async (
|
||||
slug: string,
|
||||
state: "INSTALLED" | "UPDATE" = "INSTALLED"
|
||||
) => {
|
||||
// wait 30 seconds for the badge to show up
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const badge = await findByTestId(`install-badge-${slug}`);
|
||||
expect(badge).toExist();
|
||||
|
||||
// verify a notification matching the regex is shown and then close it
|
||||
const verifyAndCloseNotification = async (regex: RegExp) => {
|
||||
await expect(await screen.findByText(regex, {}, { timeout: 30000, interval: 1000 })).toExist();
|
||||
await closeNotification();
|
||||
const badgeText = (await badge.getText()).replace(/\n/g, " ");
|
||||
console.log(`verifying installed badge for ${slug}: ${badgeText}`);
|
||||
|
||||
if (badgeText.includes(state)) {
|
||||
return;
|
||||
}
|
||||
await sleep(1000);
|
||||
}
|
||||
|
||||
throw new Error(`Failed to find installed badge for ${slug}`);
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -119,7 +122,6 @@ export function setupUtils(browser: WebdriverIO.Browser) {
|
|||
uninstallPackageIfNeeded,
|
||||
installLatestVersion,
|
||||
installSpecificVersion,
|
||||
closeNotification,
|
||||
verifyAndCloseNotification
|
||||
verifyInstalledBadge
|
||||
};
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -68,4 +68,9 @@
|
|||
<glyph glyph-name="link" unicode="6" d="M384 384l-69 0c24-16 47-45 54-64l15 0c32 0 64-32 64-64 0-32-33-64-64-64l-96 0c-32 0-64 32-64 64 0 11 3 22 9 32l-69 0c-2-11-4-21-4-32 0-64 64-128 128-128 64 0 32 0 96 0 64 0 128 64 128 128 0 64-64 128-128 128z m-240-192l-15 0c-33 0-64 32-64 64 0 32 32 64 64 64l96 0c31 0 64-32 64-64 0-12-4-23-9-32l68 0c3 10 4 21 4 32 0 64-63 128-127 128-64 0-33 0-97 0-64 0-128-64-128-128 0-64 64-128 128-128l69 0c-24 16-46 44-53 64z"/>
|
||||
<glyph glyph-name="pencil" unicode="7" d="M140 73l26 26-67 67-26-26 0-30 37 0 0-37z m150 265c0 4-2 7-7 7-1 0-3-1-4-2l-155-155c-2-2-2-3-2-5 0-4 2-6 6-6 2 0 4 0 5 2l155 154c1 2 2 3 2 5z m-16 55l119-119-238-237-118 0 0 118z m195-27c0-10-3-19-10-26l-48-47-118 118 47 48c7 7 15 10 26 10 10 0 18-3 26-10l67-67c7-8 10-16 10-26z"/>
|
||||
<glyph glyph-name="share-1" unicode="8" d="M349 419l-33-33-37 37 0-260-46 0 0 260-37-37-33 33 93 93z m93-116l0-256c0-26-21-47-46-47l-280 0c-25 0-46 21-46 47l0 256c0 25 21 46 46 46l70 0 0-46-70 0 0-256 280 0 0 256-70 0 0 46 70 0c25 0 46-21 46-46z"/>
|
||||
<glyph glyph-name="chevron-right" unicode="9" d="M389 261l-212-212c-3-3-7-5-12-5-5 0-10 2-13 5l-48 48c-3 3-5 8-5 13 0 5 2 9 5 13l152 151-152 152c-3 4-5 8-5 13 0 5 2 9 5 13l48 47c3 4 8 6 13 6 5 0 9-2 12-6l212-212c4-3 6-8 6-13 0-5-2-9-6-13z"/>
|
||||
<glyph glyph-name="right-open-mini" unicode="!" d="M195 169c0 0 81 87 81 87 0 0-81 88-81 88-9 9-9 17 0 25 9 9 17 9 24 0 0 0 99-100 99-100 8-9 8-17 0-25 0 0-99-100-99-100-7-8-15-8-24 0-9 8-9 16 0 25"/>
|
||||
<glyph glyph-name="facebook" unicode=""" d="M384 509l0-76-45 0c-17 0-28-3-33-10-6-7-9-17-9-31l0-54 84 0-11-85-73 0 0-216-87 0 0 216-73 0 0 85 73 0 0 62c0 36 10 63 29 83 20 19 47 29 80 29 28 0 49-1 65-3z"/>
|
||||
<glyph glyph-name="linkedin" unicode="#" d="M136 333l0-283-94 0 0 283z m6 88c0-14-4-26-14-35-10-9-23-14-39-14l0 0c-16 0-29 5-38 14-10 9-14 21-14 35 0 14 4 26 14 35 10 9 23 14 39 14 16 0 28-5 38-14 9-9 14-21 14-35z m333-208l0-163-94 0 0 152c0 20-3 35-11 47-8 11-20 17-36 17-12 0-22-4-30-10-9-7-15-15-19-25-2-5-3-13-3-23l0-158-94 0c1 76 1 138 1 185 0 47 0 76 0 85l-1 13 94 0 0-41 0 0c4 6 7 12 11 16 4 5 10 10 17 15 6 5 15 10 24 13 10 3 21 4 33 4 33 0 59-11 79-32 20-22 29-54 29-95z"/>
|
||||
<glyph glyph-name="exlamation-outline" unicode="$" d="M256 0c-142 0-256 114-256 256 0 142 114 256 256 256 142 0 256-114 256-256 0-142-114-256-256-256z m0 482c-124 0-226-102-226-226 0-124 102-226 226-226 124 0 226 102 226 226 0 124-102 226-226 226z m-32-98l64 0 0-160-64 0z m0-191l64 0 0-63-64 0z"/>
|
||||
</font></defs></svg>
|
||||
|
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
Binary file not shown.
Binary file not shown.
|
@ -220,3 +220,18 @@
|
|||
.icon-share-1:before {
|
||||
content: "\38";
|
||||
}
|
||||
.icon-chevron-right:before {
|
||||
content: "\39";
|
||||
}
|
||||
.icon-right-open-mini:before {
|
||||
content: "\21";
|
||||
}
|
||||
.icon-facebook:before {
|
||||
content: "\22";
|
||||
}
|
||||
.icon-linkedin:before {
|
||||
content: "\23";
|
||||
}
|
||||
.icon-exlamation-outline:before {
|
||||
content: "\24";
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ importers:
|
|||
'@testing-library/svelte': ^3.2.2
|
||||
'@testing-library/webdriverio': ^3.2.1
|
||||
'@types/bcryptjs': ^2.4.2
|
||||
'@types/canvas-confetti': ^1.6.0
|
||||
'@types/electron': ^1.6.10
|
||||
'@types/js-yaml': ^4.0.5
|
||||
'@types/mixpanel-browser': ^2.38.1
|
||||
|
@ -49,6 +50,7 @@ importers:
|
|||
axios: ^1.3.2
|
||||
bcryptjs: ^2.4.3
|
||||
buffer: ^6.0.3
|
||||
canvas-confetti: ^1.6.0
|
||||
chokidar: ^3.5.3
|
||||
concurrently: ^7.6.0
|
||||
cross-env: ^7.0.3
|
||||
|
@ -113,6 +115,7 @@ importers:
|
|||
axios: 1.4.0
|
||||
bcryptjs: 2.4.3
|
||||
buffer: 6.0.3
|
||||
canvas-confetti: 1.6.0
|
||||
chokidar: 3.5.3
|
||||
custom-electron-titlebar: 4.2.0-beta.0_electron@22.1.0
|
||||
dayjs: 1.11.7
|
||||
|
@ -153,6 +156,7 @@ importers:
|
|||
'@testing-library/svelte': 3.2.2_svelte@3.59.1
|
||||
'@testing-library/webdriverio': 3.2.1
|
||||
'@types/bcryptjs': 2.4.2
|
||||
'@types/canvas-confetti': 1.6.0
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/mixpanel-browser': 2.38.1
|
||||
'@types/testing-library__jest-dom': 5.14.5
|
||||
|
@ -3766,6 +3770,10 @@ packages:
|
|||
'@types/node': 20.1.1
|
||||
'@types/responselike': 1.0.0
|
||||
|
||||
/@types/canvas-confetti/1.6.0:
|
||||
resolution: {integrity: sha512-Yq6rIccwcco0TLD5SMUrIM7Fk7Fe/C0jmNRxJJCLtAF6gebDkPuUjK5EHedxecm69Pi/aA+It39Ux4OHmFhjRw==}
|
||||
dev: true
|
||||
|
||||
/@types/chai-subset/1.3.3:
|
||||
resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
|
||||
dependencies:
|
||||
|
@ -5314,6 +5322,10 @@ packages:
|
|||
/caniuse-lite/1.0.30001486:
|
||||
resolution: {integrity: sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==}
|
||||
|
||||
/canvas-confetti/1.6.0:
|
||||
resolution: {integrity: sha512-ej+w/m8Jzpv9Z7W7uJZer14Ke8P2ogsjg4ZMGIuq4iqUOqY2Jq8BNW42iGmNfRwREaaEfFIczLuZZiEVSYNHAA==}
|
||||
dev: false
|
||||
|
||||
/catharsis/0.9.0:
|
||||
resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==}
|
||||
engines: {node: '>= 10'}
|
||||
|
|
Loading…
Reference in a new issue