Add colours and Sentry feedback form
This commit is contained in:
parent
3ff04eba7e
commit
d6da36314d
10 changed files with 294 additions and 147 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { SENTRY_DSN } from '$lib/config';
|
||||
import { init as initSentry, handleErrorWithSentry, makeBrowserOfflineTransport, makeFetchTransport } from '@sentry/sveltekit';
|
||||
import { init as initSentry, handleErrorWithSentry, makeBrowserOfflineTransport, makeFetchTransport, feedbackIntegration } from '@sentry/sveltekit';
|
||||
|
||||
initSentry({
|
||||
dsn: SENTRY_DSN,
|
||||
|
|
@ -14,8 +14,9 @@ initSentry({
|
|||
// sessions when an error occurs.
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
|
||||
// If you don't want to use Session Replay, just remove the line below:
|
||||
// integrations: [replayIntegration()],
|
||||
integrations: [feedbackIntegration({
|
||||
autoInject: false,
|
||||
})],
|
||||
|
||||
// To enable offline events caching, use makeBrowserOfflineTransport to wrap
|
||||
// existing transports and queue events using the browsers' IndexedDB storage
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@
|
|||
--code-background-color: #0d1117;
|
||||
--code-color: #fff;
|
||||
|
||||
--color-primary: hsl(230, 50%, 90%);
|
||||
--color-secondary: hsl(230, 50%, 10%);
|
||||
--color-tertiary: hsl(290, 80%, 20%);
|
||||
--color-accent: hsl(227, 80%, 20%);
|
||||
|
||||
--color-red: #e93147;
|
||||
--color-orange: #ec7500;
|
||||
--color-yellow: #e0ac00;
|
||||
|
|
@ -60,6 +65,11 @@
|
|||
--font-color: rgba(255, 255, 255, .87);
|
||||
--font-color-contrast: rgba(0, 0, 0, .87);
|
||||
--font-color-secondary: rgba(255, 255, 255, .6);
|
||||
|
||||
--color-primary: hsl(230, 50%, 10%);
|
||||
--color-secondary: hsl(230, 50%, 90%);
|
||||
--color-tertiary: hsl(290, 80%, 80%);
|
||||
--color-accent: hsl(195, 80%, 80%);
|
||||
|
||||
--color-red: #fb464c;
|
||||
--color-orange: #e9973f;
|
||||
|
|
@ -109,7 +119,8 @@ html {
|
|||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
iframe,
|
||||
|
|
@ -125,6 +136,13 @@ svg {
|
|||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
a:visited {
|
||||
color: var(--color-tertiary);
|
||||
}
|
||||
|
||||
.container {
|
||||
--container-max-width: var(--page-width);
|
||||
--padding-x: var(--spacing);
|
||||
|
|
@ -176,3 +194,18 @@ svg {
|
|||
}
|
||||
|
||||
|
||||
button {
|
||||
display: inline-block;
|
||||
color: var(--color-primary);
|
||||
background: var(--color-accent);
|
||||
border-radius: 4px;
|
||||
margin: 8px 8px 8px 0px;
|
||||
padding: 12px 24px;
|
||||
border: solid 2px var(--color-accent);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
border: solid 2px var(--color-accent);
|
||||
color: var(--color-accent);
|
||||
background: transparent;
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
import { bookmarkify, parseMeta } from "./bookmarklets";
|
||||
import type { Config } from "./config";
|
||||
import { init } from "$lib/workers/terser";
|
||||
import { SITE_URL } from '$lib/metadata';
|
||||
import { SITE_URL } from "$lib/metadata";
|
||||
|
||||
/** @type {import('./$types').Snapshot<string>} */
|
||||
export const snapshot = {
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
restore: (v: string) => (value = v),
|
||||
};
|
||||
|
||||
let minify = init().minify
|
||||
let minify = init().minify;
|
||||
|
||||
let value = "";
|
||||
let output = "";
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
let contentAttributes = {"aria-label": "Bookmarklet editor"}
|
||||
let contentAttributes = { "aria-label": "Bookmarklet editor" };
|
||||
|
||||
$: progress = process(value);
|
||||
</script>
|
||||
|
|
@ -36,34 +36,45 @@
|
|||
description="Make booklets in your browser with this tool. Make handy shortcuts to save time."
|
||||
canonical={SITE_URL + "/bookmarklets"}
|
||||
/>
|
||||
<h1>Bookmarklet Maker</h1>
|
||||
<Editor
|
||||
{value}
|
||||
on:change={(e) => (value = e.detail)}
|
||||
lang={javascript()}
|
||||
{contentAttributes}
|
||||
>
|
||||
<div slot="header" class="code-header">Input</div>
|
||||
</Editor>
|
||||
|
||||
<h2>Output</h2>
|
||||
{#await progress}
|
||||
<p>...waiting</p>
|
||||
{:catch error}
|
||||
<p style="color: red">{error.message}</p>
|
||||
{/await}
|
||||
<label for="output">Bookmarklet code</label>
|
||||
<textarea name="output" id="output" class="output card" rows="1" value={output} readonly
|
||||
></textarea>
|
||||
<main class="main container" id="page-content">
|
||||
<h1>Bookmarklet Maker</h1>
|
||||
<Editor
|
||||
{value}
|
||||
on:change={(e) => (value = e.detail)}
|
||||
lang={javascript()}
|
||||
{contentAttributes}
|
||||
>
|
||||
<div slot="header" class="code-header">Input</div>
|
||||
</Editor>
|
||||
|
||||
<!-- <Editor readonly={true} /> -->
|
||||
<p>
|
||||
Bookmark this link: <a href={output}>{options.name || "My Bookmarklet"}</a>
|
||||
</p>
|
||||
<p>
|
||||
Either drag the link to your bookmarlets bar or, on FireFox, right click and
|
||||
select "Bookmark Link".
|
||||
</p>
|
||||
<h2>Output</h2>
|
||||
{#await progress}
|
||||
<p>...waiting</p>
|
||||
{:catch error}
|
||||
<p style="color: red">{error.message}</p>
|
||||
{/await}
|
||||
<label for="output">Bookmarklet code</label>
|
||||
<textarea
|
||||
name="output"
|
||||
id="output"
|
||||
class="output card"
|
||||
rows="1"
|
||||
value={output}
|
||||
readonly
|
||||
></textarea>
|
||||
|
||||
<!-- <Editor readonly={true} /> -->
|
||||
<p>
|
||||
Bookmark this link: <a href={output}
|
||||
>{options.name || "My Bookmarklet"}</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
Either drag the link to your bookmarlets bar or, on FireFox, right click
|
||||
and select "Bookmark Link".
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.code-header {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import SvelteSeo from "svelte-seo";
|
||||
import { init } from "$lib/workers/terser";
|
||||
import { SITE_URL } from '$lib/metadata';
|
||||
import { SITE_URL } from "$lib/metadata";
|
||||
|
||||
/** @type {import('./$types').Snapshot<string>} */
|
||||
export const snapshot = {
|
||||
|
|
@ -11,24 +11,24 @@
|
|||
restore: (v: string) => (value = v),
|
||||
};
|
||||
|
||||
let minify = init().minify
|
||||
let minify = init().minify;
|
||||
|
||||
let value = "";
|
||||
let output = "";
|
||||
async function process(str: string) {
|
||||
if (value === "") {
|
||||
output = "";
|
||||
return
|
||||
return;
|
||||
}
|
||||
let result = await minify(str)
|
||||
let result = await minify(str);
|
||||
if (typeof result.code == "string") {
|
||||
output = result.code
|
||||
output = result.code;
|
||||
} else {
|
||||
console.error(result)
|
||||
console.error(result);
|
||||
}
|
||||
}
|
||||
|
||||
let contentAttributes = {"aria-label": "Javascript editor"}
|
||||
let contentAttributes = { "aria-label": "Javascript editor" };
|
||||
|
||||
$: progress = process(value);
|
||||
</script>
|
||||
|
|
@ -38,26 +38,34 @@
|
|||
description="Reduce JavaScript code size with this handy online tool. It's easy to minify your JavaScript code."
|
||||
canonical={SITE_URL + "/javascript-minifier"}
|
||||
/>
|
||||
<h1>Javascript Minifier</h1>
|
||||
<Editor
|
||||
{value}
|
||||
on:change={(e) => (value = e.detail)}
|
||||
lang={javascript()}
|
||||
{contentAttributes}
|
||||
>
|
||||
<div slot="header" class="code-header">Input</div>
|
||||
</Editor>
|
||||
|
||||
<h2>Output</h2>
|
||||
{#await progress}
|
||||
<p>...waiting</p>
|
||||
{:catch error}
|
||||
<p style="color: red">{error.message}</p>
|
||||
{/await}
|
||||
<label for="output">Minified code</label>
|
||||
<textarea name="output" id="output" class="output card" rows="1" value={output} readonly
|
||||
></textarea>
|
||||
<main class="main container" id="page-content">
|
||||
<h1>Javascript Minifier</h1>
|
||||
<Editor
|
||||
{value}
|
||||
on:change={(e) => (value = e.detail)}
|
||||
lang={javascript()}
|
||||
{contentAttributes}
|
||||
>
|
||||
<div slot="header" class="code-header">Input</div>
|
||||
</Editor>
|
||||
|
||||
<h2>Output</h2>
|
||||
{#await progress}
|
||||
<p>...waiting</p>
|
||||
{:catch error}
|
||||
<p style="color: red">{error.message}</p>
|
||||
{/await}
|
||||
<label for="output">Minified code</label>
|
||||
<textarea
|
||||
name="output"
|
||||
id="output"
|
||||
class="output card"
|
||||
rows="1"
|
||||
value={output}
|
||||
readonly
|
||||
></textarea>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.code-header {
|
||||
|
|
|
|||
78
packages/website/src/routes/+error.svelte
Normal file
78
packages/website/src/routes/+error.svelte
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
import * as Sentry from "@sentry/sveltekit";
|
||||
import { onMount } from "svelte";
|
||||
import SvelteSeo from "svelte-seo";
|
||||
|
||||
const { status, error } = $page;
|
||||
const message = error?.message || "Hmm";
|
||||
const title = `${status}: ${message}`;
|
||||
let sentryElement: HTMLDivElement;
|
||||
let openForm = () => {};
|
||||
onMount(async () => {
|
||||
const feedback = Sentry.getFeedback({
|
||||
el: sentryElement,
|
||||
});
|
||||
if (!feedback) {
|
||||
return;
|
||||
}
|
||||
// console.log("feedback", feedback);
|
||||
const form = await feedback.createForm({});
|
||||
form.appendToDom();
|
||||
form.open();
|
||||
openForm = async () => {
|
||||
form.open();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<SvelteSeo {title} />
|
||||
|
||||
<main class="main container" id="page-content">
|
||||
<div class="wrapper">
|
||||
<h1>{title}</h1>
|
||||
<div bind:this={sentryElement} class="feedback"></div>
|
||||
<button on:click={openForm}>Send Feedback</button>
|
||||
<button class="secondary" on:click={()=> window.location.reload()}>Reload</button>
|
||||
<!-- <div role="doc-subtitle">{status}</div> -->
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.wrapper {
|
||||
flex-grow: 2;
|
||||
width: 40vw;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
/* display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing);
|
||||
margin: 48px auto;
|
||||
max-width: 320px;
|
||||
padding: 3rem 0.5rem; */
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 4em;
|
||||
/* font-weight: 100; */
|
||||
}
|
||||
/* [role="doc-subtitle"] {
|
||||
padding-block-start: 0;
|
||||
font-size: 2em;
|
||||
font-weight: 600;
|
||||
} */
|
||||
:global(#sentry-feedback) {
|
||||
--dialog-inset: auto auto 0;
|
||||
}
|
||||
p {
|
||||
width: 95%;
|
||||
font-size: 1.5em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -11,7 +11,4 @@
|
|||
<meta property="og:site_name" content={SITE_TITLE}>
|
||||
</svelte:head>
|
||||
<Nav />
|
||||
<main class="main container" id="page-content">
|
||||
<slot />
|
||||
|
||||
</main>
|
||||
<slot />
|
||||
|
|
@ -6,15 +6,27 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="alternate" type="application/rss+xml" title={SITE_TITLE} href={SITE_URL + "/blog/rss.xml"}>
|
||||
<link rel="alternate" type="application/feed+json" title={SITE_TITLE} href={SITE_URL + "/blog/feed.json"}>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title={SITE_TITLE}
|
||||
href={SITE_URL + "/blog/rss.xml"}
|
||||
/>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/feed+json"
|
||||
title={SITE_TITLE}
|
||||
href={SITE_URL + "/blog/feed.json"}
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<SvelteSeo
|
||||
title="Jade Ellis"
|
||||
description="Student, Creative & Computer Scientist. See what I'm doing."
|
||||
canonical="https://jade.ellis.link"
|
||||
title="Jade Ellis"
|
||||
description="Student, Creative & Computer Scientist. See what I'm doing."
|
||||
canonical="https://jade.ellis.link"
|
||||
/>
|
||||
<Hero />
|
||||
|
||||
<Homepage />
|
||||
<main class="main container" id="page-content">
|
||||
<Hero />
|
||||
<Homepage />
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -53,44 +53,46 @@
|
|||
|
||||
<SvelteSeo title="Jade's Blog - Posts" canonical={SITE_URL + "/blog"} />
|
||||
|
||||
<section role="feed" class="h-feed" id="feed">
|
||||
<h1 class="p-name">
|
||||
<a
|
||||
aria-hidden="true"
|
||||
tabindex="-1"
|
||||
class="u-url permalink"
|
||||
href={SITE_URL + "/blog#feed"}>#</a
|
||||
>Jade's Blog - Posts
|
||||
</h1>
|
||||
{#each pages as post, index}
|
||||
<article
|
||||
aria-posinset={index + 1}
|
||||
aria-setsize={pages.length}
|
||||
class="h-entry"
|
||||
>
|
||||
<div class="content" data-sveltekit-preload-data="hover">
|
||||
<h2>
|
||||
<a class="u-url p-name" href="/blog/{post.canonical}">
|
||||
{post.title}
|
||||
</a>
|
||||
</h2>
|
||||
<span class="quiet"
|
||||
><time class="dt-published" datetime={post.date}
|
||||
>{new Date(post.date).toLocaleDateString()}</time
|
||||
></span
|
||||
>
|
||||
{#if post.description}
|
||||
<p class="p-summary">{post.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</article>
|
||||
{:else}
|
||||
<p>No posts yet!</p>
|
||||
{/each}
|
||||
<!-- {#if showPosts < postCount}
|
||||
<main class="main container" id="page-content">
|
||||
<section role="feed" class="h-feed" id="feed">
|
||||
<h1 class="p-name">
|
||||
<a
|
||||
aria-hidden="true"
|
||||
tabindex="-1"
|
||||
class="u-url permalink"
|
||||
href={SITE_URL + "/blog#feed"}>#</a
|
||||
>Jade's Blog - Posts
|
||||
</h1>
|
||||
{#each pages as post, index}
|
||||
<article
|
||||
aria-posinset={index + 1}
|
||||
aria-setsize={pages.length}
|
||||
class="h-entry"
|
||||
>
|
||||
<div class="content" data-sveltekit-preload-data="hover">
|
||||
<h2>
|
||||
<a class="u-url p-name" href="/blog/{post.canonical}">
|
||||
{post.title}
|
||||
</a>
|
||||
</h2>
|
||||
<span class="quiet"
|
||||
><time class="dt-published" datetime={post.date}
|
||||
>{new Date(post.date).toLocaleDateString()}</time
|
||||
></span
|
||||
>
|
||||
{#if post.description}
|
||||
<p class="p-summary">{post.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</article>
|
||||
{:else}
|
||||
<p>No posts yet!</p>
|
||||
{/each}
|
||||
<!-- {#if showPosts < postCount}
|
||||
<button type="submit" on:click={handleClick}>See more {H_ELLIPSIS_ENTITY}</button>
|
||||
{/if} -->
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.permalink {
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@
|
|||
});
|
||||
|
||||
gtag("event", "share", {
|
||||
"share_url": url.href,
|
||||
"share_title": data.post.title,
|
||||
"share_button": "article_top",
|
||||
"method": "navigator_share",
|
||||
"content_type": "article",
|
||||
share_url: url.href,
|
||||
share_title: data.post.title,
|
||||
share_button: "article_top",
|
||||
method: "navigator_share",
|
||||
content_type: "article",
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.toString().includes("AbortError")) {
|
||||
|
|
@ -176,42 +176,44 @@
|
|||
}}
|
||||
/>
|
||||
|
||||
<article class="h-entry">
|
||||
<h1 id="title" class="p-name">{data.post.title}</h1>
|
||||
<aside>
|
||||
<a class="u-url ib" href={canonical}
|
||||
>Published on <time
|
||||
class="dt-published ib"
|
||||
datetime={data.post.date}
|
||||
>{new Date(data.post.date).toLocaleDateString()}</time
|
||||
></a
|
||||
>
|
||||
<span class="author p-author h-card vcard ib">
|
||||
by <img
|
||||
loading="lazy"
|
||||
style="display: none;"
|
||||
src={defaultAuthor.image}
|
||||
class="avatar avatar-96 photo u-photo"
|
||||
/><a class="u-url url fn n p-name" href={defaultAuthor.url}
|
||||
>{defaultAuthor.name}</a
|
||||
></span
|
||||
>
|
||||
· <span class="reading-time ib">{data.post.readingTime.text}</span>
|
||||
{#if webShareAPISupported}
|
||||
· <button class="link" on:click={handleWebShare}>Share</button>
|
||||
{/if}
|
||||
</aside>
|
||||
<Toc headings={data.post.headings} />
|
||||
<!-- {#await GhReleasesDownload}
|
||||
<main class="main container" id="page-content">
|
||||
<article class="h-entry">
|
||||
<h1 id="title" class="p-name">{data.post.title}</h1>
|
||||
<aside>
|
||||
<a class="u-url ib" href={canonical}
|
||||
>Published on <time
|
||||
class="dt-published ib"
|
||||
datetime={data.post.date}
|
||||
>{new Date(data.post.date).toLocaleDateString()}</time
|
||||
></a
|
||||
>
|
||||
<span class="author p-author h-card vcard ib">
|
||||
by <img
|
||||
loading="lazy"
|
||||
style="display: none;"
|
||||
src={defaultAuthor.image}
|
||||
class="avatar avatar-96 photo u-photo"
|
||||
/><a class="u-url url fn n p-name" href={defaultAuthor.url}
|
||||
>{defaultAuthor.name}</a
|
||||
></span
|
||||
>
|
||||
· <span class="reading-time ib">{data.post.readingTime.text}</span>
|
||||
{#if webShareAPISupported}
|
||||
· <button class="link" on:click={handleWebShare}>Share</button>
|
||||
{/if}
|
||||
</aside>
|
||||
<Toc headings={data.post.headings} />
|
||||
<!-- {#await GhReleasesDownload}
|
||||
|
||||
{:then component}
|
||||
<svelte:component this={component} releaseData={data.ghReleaseData} />
|
||||
{/await} -->
|
||||
|
||||
<div class="e-content">
|
||||
<svelte:component this={data.component} />
|
||||
</div>
|
||||
</article>
|
||||
<div class="e-content">
|
||||
<svelte:component this={data.component} />
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
aside {
|
||||
|
|
|
|||
|
|
@ -18,15 +18,18 @@
|
|||
description={data.post.description}
|
||||
canonical={SITE_URL + "/projects/" + data.post.slug}
|
||||
/>
|
||||
<h1>{data.post.title}</h1>
|
||||
<!-- {#await GhReleasesDownload}
|
||||
|
||||
<main class="main container" id="page-content">
|
||||
<h1>{data.post.title}</h1>
|
||||
<!-- {#await GhReleasesDownload}
|
||||
|
||||
{:then component}
|
||||
<svelte:component this={component} releaseData={data.ghReleaseData} />
|
||||
{/await} -->
|
||||
|
||||
{#if data.ghReleaseData}
|
||||
<GhReleasesDownload releaseData={data.ghReleaseData} />
|
||||
{/if}
|
||||
{#if data.ghReleaseData}
|
||||
<GhReleasesDownload releaseData={data.ghReleaseData} />
|
||||
{/if}
|
||||
|
||||
<svelte:component this={data.component} />
|
||||
<svelte:component this={data.component} />
|
||||
</main>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue