This commit is contained in:
Jesse Winton
2024-08-27 09:43:37 -04:00
parent c9f7fc75f1
commit 562058cf0e
319 changed files with 16360 additions and 15622 deletions

View File

@@ -1,30 +1,30 @@
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:svelte/recommended",
"prettier",
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
parserOptions: {
sourceType: 'module',
sourceType: "module",
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
extraFileExtensions: [".svelte"],
},
env: {
browser: true,
es2017: true,
node: true
node: true,
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
files: ["*.svelte"],
parser: "svelte-eslint-parser",
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
parser: "@typescript-eslint/parser",
},
},
],
};

View File

@@ -12,11 +12,11 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
repository: 'meilisearch/scrapix'
repository: "meilisearch/scrapix"
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
node-version: "20.x"
- run: yarn
- run: yarn start -c "$SCRAPIX_CONFIG"
env:

View File

@@ -2,7 +2,7 @@ name: Mark stale issues
on:
schedule:
- cron: '0 0 * * *' # Midnight Runtime
- cron: "0 0 * * *" # Midnight Runtime
jobs:
stale:
@@ -13,11 +13,11 @@ jobs:
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue has been labeled as a 'question', indicating that it requires additional information from the requestor. It has been inactive for 7 days. If no further activity occurs, this issue will be closed in 14 days."
stale-issue-label: 'stale'
stale-issue-label: "stale"
days-before-stale: 7
days-before-close: 14
remove-stale-when-updated: true
close-issue-message: 'This issue has been closed due to inactivity. If you still require assistance, please provide the requested information.'
close-issue-reason: 'not_planned'
close-issue-message: "This issue has been closed due to inactivity. If you still require assistance, please provide the requested information."
close-issue-reason: "not_planned"
operations-per-run: 100
only-labels: 'question'
only-labels: "question"

View File

@@ -2,7 +2,7 @@ name: Tests
on:
pull_request_target:
branches: ['**']
branches: ["**"]
permissions: read-all
@@ -35,7 +35,7 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Build Website
env:
NODE_OPTIONS: '--max_old_space_size=8192'
NODE_OPTIONS: "--max_old_space_size=8192"
PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.PUBLIC_APPWRITE_PROJECT_ID }}
PUBLIC_APPWRITE_DB_MAIN_ID: ${{ secrets.PUBLIC_APPWRITE_DB_MAIN_ID }}
PUBLIC_APPWRITE_COL_THREADS_ID: ${{ secrets.PUBLIC_APPWRITE_COL_THREADS_ID }}

View File

@@ -1,9 +1,9 @@
x-logging: &x-logging
logging:
driver: 'json-file'
driver: "json-file"
options:
max-file: '5'
max-size: '20m'
max-file: "5"
max-size: "20m"
x-update-config: &x-update-config
update_config:
@@ -17,7 +17,7 @@ x-update-config: &x-update-config
parallelism: 2
order: stop-first
version: '3.8'
version: "3.8"
services:
traefik:

View File

@@ -1,9 +1,9 @@
x-logging: &x-logging
logging:
driver: 'json-file'
driver: "json-file"
options:
max-file: '5'
max-size: '20m'
max-file: "5"
max-size: "20m"
x-update-config: &x-update-config
update_config:
@@ -17,7 +17,7 @@ x-update-config: &x-update-config
parallelism: 2
order: stop-first
version: '3.8'
version: "3.8"
services:
traefik:

View File

@@ -1,12 +1,12 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import type { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173
command: "npm run build && npm run preview",
port: 4173,
},
testDir: 'tests',
testMatch: /(.+\.)?(test|spec)\.[jt]s/
testDir: "tests",
testMatch: /(.+\.)?(test|spec)\.[jt]s/,
};
export default config;

View File

@@ -1,5 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {}
}
"@tailwindcss/postcss": {},
},
};

View File

@@ -1,5 +1,5 @@
import { build } from 'vite';
import { downloadContributors } from './download-contributor-data.js';
import { build } from "vite";
import { downloadContributors } from "./download-contributor-data.js";
async function main() {
await downloadContributors();

View File

@@ -1,4 +1,4 @@
import fs from 'fs';
import fs from "fs";
const perPage = 100;
@@ -6,7 +6,7 @@ const outputFile = `./src/lib/contributors.ts`;
const headers = process.env.GITHUB_TOKEN
? {
Authorization: `token ${process.env.GITHUB_TOKEN}`
Authorization: `token ${process.env.GITHUB_TOKEN}`,
}
: {};
@@ -22,7 +22,7 @@ async function fetchRepositories() {
const url = `https://api.github.com/orgs/appwrite/repos?page=${page}&per_page=${perPage}`;
const response = await fetch(url, {
headers
headers,
});
if (!response.ok) {
@@ -37,7 +37,9 @@ async function fetchRepositories() {
repositoriesData = repositoriesData.concat(data);
page++;
}
console.log(`Fetched ${data.length} repositories. Total: ${repositoriesData.length}...\n`);
console.log(
`Fetched ${data.length} repositories. Total: ${repositoriesData.length}...\n`,
);
}
return repositoriesData.map((repo) => repo.full_name);
@@ -52,7 +54,7 @@ async function fetchContributors(apiUrl) {
console.log(`Fetching page ${page} of contributors...`);
const url = `${apiUrl}?page=${page}&per_page=${perPage}`;
const response = await fetch(url, {
headers
headers,
});
if (!response.ok) {
@@ -69,7 +71,9 @@ async function fetchContributors(apiUrl) {
page++;
}
console.log(`Fetched ${data.length} contributors. Total: ${contributorsData.length}...\n`);
console.log(
`Fetched ${data.length} contributors. Total: ${contributorsData.length}...\n`,
);
}
return contributorsData;
@@ -78,7 +82,11 @@ async function fetchContributors(apiUrl) {
export async function downloadContributors() {
const contributors = new Set();
for (const repo of ['appwrite/appwrite', 'appwrite/console', 'appwrite/sdk-generator']) {
for (const repo of [
"appwrite/appwrite",
"appwrite/console",
"appwrite/sdk-generator",
]) {
console.log(`Fetching contributors for ${repo}...`);
const url = `https://api.github.com/repos/${repo}/contributors`;
const data = await fetchContributors(url);
@@ -90,9 +98,9 @@ export async function downloadContributors() {
const contributorsString = JSON.stringify(contributorsArray, null, 4);
const contributorsFile = `export const contributors = ${contributorsString};`;
const currentContributors = fs.readFileSync(outputFile, 'utf8');
const currentContributors = fs.readFileSync(outputFile, "utf8");
if (currentContributors.length >= contributorsFile.length) {
console.log('No new contributors found. Exiting...');
console.log("No new contributors found. Exiting...");
return;
}

View File

@@ -1,11 +1,11 @@
import { readdirSync, statSync } from 'fs';
import { join, relative } from 'path';
import sharp from 'sharp';
import { fileURLToPath } from 'url';
import { readdirSync, statSync } from "fs";
import { join, relative } from "path";
import sharp from "sharp";
import { fileURLToPath } from "url";
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const root_dir = join(__dirname, '../static');
const exceptions = ['assets/'];
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const root_dir = join(__dirname, "../static");
const exceptions = ["assets/"];
/**
* @type {{
@@ -18,20 +18,20 @@ const exceptions = ['assets/'];
*/
const config = {
jpeg: {
quality: 100
quality: 100,
},
webp: {
lossless: true
lossless: true,
},
png: {
quality: 100
quality: 100,
},
gif: {
quality: 100
quality: 100,
},
avif: {
lossless: true
}
lossless: true,
},
};
/** @type {sharp.ResizeOptions} */
@@ -39,7 +39,7 @@ const resize_config = {
width: 1280,
height: 1280,
fit: sharp.fit.inside,
withoutEnlargement: true
withoutEnlargement: true,
};
function* walk_directory(dir) {
@@ -57,7 +57,7 @@ function* walk_directory(dir) {
}
function is_image(file) {
const extension = file.split('.').pop().toLowerCase();
const extension = file.split(".").pop().toLowerCase();
return Object.keys(config).includes(extension);
}
@@ -67,14 +67,15 @@ function get_relative_path(file) {
}
async function main() {
for (const file of walk_directory(join(__dirname, '../static'))) {
for (const file of walk_directory(join(__dirname, "../static"))) {
const relative_path = get_relative_path(file);
const is_animated = file.endsWith('.gif');
const is_animated = file.endsWith(".gif");
if (!is_image(file)) continue;
if (exceptions.some((exception) => relative_path.startsWith(exception))) continue;
if (exceptions.some((exception) => relative_path.startsWith(exception)))
continue;
const image = sharp(file, {
animated: is_animated
animated: is_animated,
});
const size_before = (await image.toBuffer()).length;
const meta = await image.metadata();

View File

@@ -1,15 +1,15 @@
import { createApp, fromNodeMiddleware, toNodeListener } from 'h3';
import { createServer } from 'node:http';
import { handler } from '../build/handler.js';
import { sitemap } from './sitemap.js';
import { createApp, fromNodeMiddleware, toNodeListener } from "h3";
import { createServer } from "node:http";
import { handler } from "../build/handler.js";
import { sitemap } from "./sitemap.js";
async function main() {
const port = process.env.PORT || 3000;
const app = createApp();
app.use('/sitemap.xml', await sitemap());
app.use("/sitemap.xml", await sitemap());
app.use(fromNodeMiddleware(handler));
const server = createServer(toNodeListener(app)).listen(port);
server.addListener('listening', () => {
server.addListener("listening", () => {
console.log(`Listening on http://0.0.0.0:${port}`);
});
}

View File

@@ -1,16 +1,16 @@
import { createRequire } from 'node:module';
import { defineEventHandler, setResponseHeader } from 'h3';
import { createRequire } from "node:module";
import { defineEventHandler, setResponseHeader } from "h3";
/**
* @returns {Promise<import('h3').EventHandler>}
*/
export async function sitemap() {
console.info('Preparing Sitemap...');
const manifest = await import('../build/server/manifest.js');
console.info("Preparing Sitemap...");
const manifest = await import("../build/server/manifest.js");
const prerendered = manifest.prerendered;
const file_route_extensions = ['.json', '.xml'];
const file_route_extensions = [".json", ".xml"];
const routes = [...prerendered, ...collectThreads()].filter(
(route) => !file_route_extensions.some((ext) => route.endsWith(ext))
(route) => !file_route_extensions.some((ext) => route.endsWith(ext)),
);
console.info(`Sitemap loaded with ${routes.length} routes!`);
@@ -21,13 +21,13 @@ export async function sitemap() {
(route) => `<url>
<loc>https://appwrite.io${route}</loc>
</url>
`
`,
)
.join('')}
.join("")}
</urlset>`;
return defineEventHandler((event) => {
setResponseHeader(event, 'Content-Type', 'application/xml');
setResponseHeader(event, "Content-Type", "application/xml");
return sitemap;
});
@@ -37,7 +37,9 @@ export async function sitemap() {
* @returns {string[]}
*/
function collectThreads() {
const threads = createRequire(import.meta.url)('../build/prerendered/threads/data.json');
const threads = createRequire(import.meta.url)(
"../build/prerendered/threads/data.json",
);
return threads.map((id) => `/threads/${id}`);
}

View File

@@ -1,7 +1,7 @@
import { dev } from '$app/environment';
import { SENTRY_DSN } from '$lib/constants';
import { handleErrorWithSentry, replayIntegration } from '@sentry/sveltekit';
import * as Sentry from '@sentry/sveltekit';
import { dev } from "$app/environment";
import { SENTRY_DSN } from "$lib/constants";
import { handleErrorWithSentry, replayIntegration } from "@sentry/sveltekit";
import * as Sentry from "@sentry/sveltekit";
Sentry.init({
enabled: !dev,
@@ -22,9 +22,9 @@ Sentry.init({
replayIntegration({
maskAllInputs: true,
maskAllText: false,
blockAllMedia: false
})
]
blockAllMedia: false,
}),
],
});
// If you have a custom error handler, pass it to `handleErrorWithSentry`

View File

@@ -1,18 +1,20 @@
import * as Sentry from '@sentry/sveltekit';
import type { Handle } from '@sveltejs/kit';
import redirects from './redirects.json';
import { sequence } from '@sveltejs/kit/hooks';
import { BANNER_KEY, SENTRY_DSN } from '$lib/constants';
import { dev } from '$app/environment';
import * as Sentry from "@sentry/sveltekit";
import type { Handle } from "@sveltejs/kit";
import redirects from "./redirects.json";
import { sequence } from "@sveltejs/kit/hooks";
import { BANNER_KEY, SENTRY_DSN } from "$lib/constants";
import { dev } from "$app/environment";
Sentry.init({
enabled: !dev,
dsn: SENTRY_DSN,
tracesSampleRate: 1,
allowUrls: [/appwrite\.io/]
allowUrls: [/appwrite\.io/],
});
const redirectMap = new Map(redirects.map(({ link, redirect }) => [link, redirect]));
const redirectMap = new Map(
redirects.map(({ link, redirect }) => [link, redirect]),
);
const redirecter: Handle = async ({ event, resolve }) => {
const currentPath = event.url.pathname;
@@ -20,8 +22,8 @@ const redirecter: Handle = async ({ event, resolve }) => {
return new Response(null, {
status: 308,
headers: {
location: redirectMap.get(currentPath) ?? ''
}
location: redirectMap.get(currentPath) ?? "",
},
});
}
@@ -30,10 +32,15 @@ const redirecter: Handle = async ({ event, resolve }) => {
const bannerRewriter: Handle = async ({ event, resolve }) => {
const response = await resolve(event, {
transformPageChunk: ({ html }) => html.replace('%aw_banner_key%', BANNER_KEY)
transformPageChunk: ({ html }) =>
html.replace("%aw_banner_key%", BANNER_KEY),
});
return response;
};
export const handle = sequence(Sentry.sentryHandle(), redirecter, bannerRewriter);
export const handle = sequence(
Sentry.sentryHandle(),
redirecter,
bannerRewriter,
);
export const handleError = Sentry.handleErrorWithSentry();

View File

@@ -1,3 +1,3 @@
import { generateIcons } from './scripts.js';
import { generateIcons } from "./scripts.js";
generateIcons();

View File

@@ -1,3 +1,3 @@
import { optimizeSVG } from './scripts.js';
import { optimizeSVG } from "./scripts.js";
optimizeSVG();

View File

@@ -1,71 +1,178 @@
@font-face {
font-family: "web-icon";
font-display: swap;
src: url('web-icon.eot'); /* IE9*/
src: url('web-icon.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */
url("web-icon.woff2") format("woff2"),
src: url("web-icon.eot"); /* IE9*/
src:
url("web-icon.eot#iefix") format("embedded-opentype"),
/* IE6-IE8 */ url("web-icon.woff2") format("woff2"),
url("web-icon.woff") format("woff"),
url('web-icon.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('web-icon.svg#web-icon') format('svg'); /* iOS 4.1- */
url("web-icon.ttf") format("truetype"),
/* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url("web-icon.svg#web-icon") format("svg"); /* iOS 4.1- */
}
[class^="web-icon-"], [class*=" web-icon-"] {
font-family: 'web-icon' !important;
[class^="web-icon-"],
[class*=" web-icon-"] {
font-family: "web-icon" !important;
font-size: 20px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.web-icon-apple:before { content: "\ea01"; }
.web-icon-appwrite:before { content: "\ea02"; }
.web-icon-arrow-down:before { content: "\ea03"; }
.web-icon-arrow-ext-link:before { content: "\ea04"; }
.web-icon-arrow-left:before { content: "\ea05"; }
.web-icon-arrow-right:before { content: "\ea06"; }
.web-icon-arrow-up:before { content: "\ea07"; }
.web-icon-calendar:before { content: "\ea08"; }
.web-icon-check:before { content: "\ea09"; }
.web-icon-chevron-down:before { content: "\ea0a"; }
.web-icon-chevron-left:before { content: "\ea0b"; }
.web-icon-chevron-right:before { content: "\ea0c"; }
.web-icon-chevron-up:before { content: "\ea0d"; }
.web-icon-close:before { content: "\ea0e"; }
.web-icon-command:before { content: "\ea0f"; }
.web-icon-copy:before { content: "\ea10"; }
.web-icon-daily-dev:before { content: "\ea11"; }
.web-icon-dark:before { content: "\ea12"; }
.web-icon-discord:before { content: "\ea13"; }
.web-icon-divider-vertical:before { content: "\ea14"; }
.web-icon-download:before { content: "\ea15"; }
.web-icon-ext-link:before { content: "\ea16"; }
.web-icon-firebase:before { content: "\ea17"; }
.web-icon-github:before { content: "\ea18"; }
.web-icon-google:before { content: "\ea19"; }
.web-icon-hamburger-menu:before { content: "\ea1a"; }
.web-icon-light:before { content: "\ea1b"; }
.web-icon-linkedin:before { content: "\ea1c"; }
.web-icon-location:before { content: "\ea1d"; }
.web-icon-logout-left:before { content: "\ea1e"; }
.web-icon-logout-right:before { content: "\ea1f"; }
.web-icon-mailgun:before { content: "\ea20"; }
.web-icon-message:before { content: "\ea21"; }
.web-icon-microsoft:before { content: "\ea22"; }
.web-icon-minus:before { content: "\ea23"; }
.web-icon-nuxt:before { content: "\ea24"; }
.web-icon-platform:before { content: "\ea25"; }
.web-icon-play:before { content: "\ea26"; }
.web-icon-plus:before { content: "\ea27"; }
.web-icon-product-hunt:before { content: "\ea28"; }
.web-icon-refine:before { content: "\ea29"; }
.web-icon-rest:before { content: "\ea2a"; }
.web-icon-search:before { content: "\ea2b"; }
.web-icon-sendgrid:before { content: "\ea2c"; }
.web-icon-star:before { content: "\ea2d"; }
.web-icon-system:before { content: "\ea2e"; }
.web-icon-textmagic:before { content: "\ea2f"; }
.web-icon-twitter:before { content: "\ea30"; }
.web-icon-vue:before { content: "\ea31"; }
.web-icon-x:before { content: "\ea32"; }
.web-icon-ycombinator:before { content: "\ea33"; }
.web-icon-youtube:before { content: "\ea34"; }
.web-icon-apple:before {
content: "\ea01";
}
.web-icon-appwrite:before {
content: "\ea02";
}
.web-icon-arrow-down:before {
content: "\ea03";
}
.web-icon-arrow-ext-link:before {
content: "\ea04";
}
.web-icon-arrow-left:before {
content: "\ea05";
}
.web-icon-arrow-right:before {
content: "\ea06";
}
.web-icon-arrow-up:before {
content: "\ea07";
}
.web-icon-calendar:before {
content: "\ea08";
}
.web-icon-check:before {
content: "\ea09";
}
.web-icon-chevron-down:before {
content: "\ea0a";
}
.web-icon-chevron-left:before {
content: "\ea0b";
}
.web-icon-chevron-right:before {
content: "\ea0c";
}
.web-icon-chevron-up:before {
content: "\ea0d";
}
.web-icon-close:before {
content: "\ea0e";
}
.web-icon-command:before {
content: "\ea0f";
}
.web-icon-copy:before {
content: "\ea10";
}
.web-icon-daily-dev:before {
content: "\ea11";
}
.web-icon-dark:before {
content: "\ea12";
}
.web-icon-discord:before {
content: "\ea13";
}
.web-icon-divider-vertical:before {
content: "\ea14";
}
.web-icon-download:before {
content: "\ea15";
}
.web-icon-ext-link:before {
content: "\ea16";
}
.web-icon-firebase:before {
content: "\ea17";
}
.web-icon-github:before {
content: "\ea18";
}
.web-icon-google:before {
content: "\ea19";
}
.web-icon-hamburger-menu:before {
content: "\ea1a";
}
.web-icon-light:before {
content: "\ea1b";
}
.web-icon-linkedin:before {
content: "\ea1c";
}
.web-icon-location:before {
content: "\ea1d";
}
.web-icon-logout-left:before {
content: "\ea1e";
}
.web-icon-logout-right:before {
content: "\ea1f";
}
.web-icon-mailgun:before {
content: "\ea20";
}
.web-icon-message:before {
content: "\ea21";
}
.web-icon-microsoft:before {
content: "\ea22";
}
.web-icon-minus:before {
content: "\ea23";
}
.web-icon-nuxt:before {
content: "\ea24";
}
.web-icon-platform:before {
content: "\ea25";
}
.web-icon-play:before {
content: "\ea26";
}
.web-icon-plus:before {
content: "\ea27";
}
.web-icon-product-hunt:before {
content: "\ea28";
}
.web-icon-refine:before {
content: "\ea29";
}
.web-icon-rest:before {
content: "\ea2a";
}
.web-icon-search:before {
content: "\ea2b";
}
.web-icon-sendgrid:before {
content: "\ea2c";
}
.web-icon-star:before {
content: "\ea2d";
}
.web-icon-system:before {
content: "\ea2e";
}
.web-icon-textmagic:before {
content: "\ea2f";
}
.web-icon-twitter:before {
content: "\ea30";
}
.web-icon-vue:before {
content: "\ea31";
}
.web-icon-x:before {
content: "\ea32";
}
.web-icon-ycombinator:before {
content: "\ea33";
}
.web-icon-youtube:before {
content: "\ea34";
}

View File

@@ -1,15 +1,15 @@
// @ts-expect-error missing types
import SVGFixer from 'oslllo-svg-fixer';
import svgtofont from 'svgtofont';
import { resolve } from 'path';
import SVGFixer from "oslllo-svg-fixer";
import svgtofont from "svgtofont";
import { resolve } from "path";
const src = resolve(process.cwd(), 'src/icons/svg');
const optimized = resolve(process.cwd(), 'src/icons/optimized');
const dist = resolve(process.cwd(), 'src/icons/output');
const src = resolve(process.cwd(), "src/icons/svg");
const optimized = resolve(process.cwd(), "src/icons/optimized");
const dist = resolve(process.cwd(), "src/icons/output");
export const optimizeSVG = async () => {
const fixer = new SVGFixer(src, optimized, {
showProgressBar: true
showProgressBar: true,
});
await fixer.fix();
@@ -17,13 +17,13 @@ export const optimizeSVG = async () => {
export const generateIcons = async () => {
await svgtofont({
classNamePrefix: 'web-icon',
classNamePrefix: "web-icon",
src: optimized,
dist: dist,
fontName: 'web-icon',
styleTemplates: resolve(process.cwd(), 'src/icons/templates'),
fontName: "web-icon",
styleTemplates: resolve(process.cwd(), "src/icons/templates"),
css: {
fontSize: '20px'
fontSize: "20px",
},
outSVGReact: false,
svgicons2svgfont: {
@@ -32,9 +32,9 @@ export const generateIcons = async () => {
fixedWidth: true,
fontHeight: 1000,
normalize: true,
descent: 200
descent: 200,
},
emptyDist: true,
generateInfoData: true
generateInfoData: true,
});
};

View File

@@ -1,12 +1,12 @@
<script lang="ts">
export let src: string;
export let alt = '';
export let alt = "";
export let controls = true;
export let autoplay = false;
let className = '';
let className = "";
export { className as class };
const videoExtensions = ['mp4', 'webm', 'ogg'] as const;
const videoExtensions = ["mp4", "webm", "ogg"] as const;
$: isVideo = videoExtensions.some((ext) => src.endsWith(ext));
</script>

View File

@@ -1,11 +1,11 @@
<script lang="ts">
import { melt } from '@melt-ui/svelte';
import { getTabsContext } from './index.svelte';
import { melt } from "@melt-ui/svelte";
import { getTabsContext } from "./index.svelte";
export let tab: string;
const {
elements: { content }
elements: { content },
} = getTabsContext();
</script>

View File

@@ -1,5 +1,5 @@
<script lang="ts" context="module">
const CTX_KEY = 'tabs';
const CTX_KEY = "tabs";
type Context = Tabs & {
tabs: readonly string[];
@@ -15,12 +15,12 @@
</script>
<script lang="ts">
import { createTabs, melt, type Tabs } from '@melt-ui/svelte';
import { getContext, setContext } from 'svelte';
import List from './List.svelte';
import Content from './Content.svelte';
import { createTabs, melt, type Tabs } from "@melt-ui/svelte";
import { getContext, setContext } from "svelte";
import List from "./List.svelte";
import Content from "./Content.svelte";
export let tabs: Context['tabs'];
export let tabs: Context["tabs"];
export let tab = tabs[0];
const ctx = createTabs({
@@ -28,7 +28,7 @@
onValueChange({ next }) {
tab = next;
return next;
}
},
});
$: value.set(tab);
@@ -36,7 +36,7 @@
const {
elements: { root },
states: { value }
states: { value },
} = ctx;
</script>

View File

@@ -1,2 +1,2 @@
export { default as Tabs } from './Tabs/index.svelte';
export { default as Media } from './Media.svelte';
export { default as Tabs } from "./Tabs/index.svelte";
export { default as Media } from "./Media.svelte";

View File

@@ -1,15 +1,18 @@
export function clickOutside(node: HTMLElement, callback: (e: MouseEvent) => void) {
export function clickOutside(
node: HTMLElement,
callback: (e: MouseEvent) => void,
) {
const handleClick = (event: MouseEvent) => {
if (node && !node.contains(event.target as Node)) {
callback(event);
}
};
document.addEventListener('click', handleClick, true);
document.addEventListener("click", handleClick, true);
return {
destroy() {
document.removeEventListener('click', handleClick, true);
}
document.removeEventListener("click", handleClick, true);
},
};
}

View File

@@ -1,6 +1,6 @@
export const highlight = (node: HTMLElement, text: string[]) => {
text.forEach((word) => {
const regex = new RegExp(`(${word})`, 'gi');
node.innerHTML = node.innerHTML.replace(regex, '<mark>$1</mark>');
const regex = new RegExp(`(${word})`, "gi");
node.innerHTML = node.innerHTML.replace(regex, "<mark>$1</mark>");
});
};

View File

@@ -1,3 +1,3 @@
export * from './portal';
export * from './rect';
export * from './visible';
export * from "./portal";
export * from "./rect";
export * from "./visible";

View File

@@ -1,22 +1,25 @@
import { tick } from 'svelte';
import type { Action } from 'svelte/action';
import { tick } from "svelte";
import type { Action } from "svelte/action";
export type PortalConfig =
| { target?: string | HTMLElement | undefined; prepend?: boolean }
| undefined;
const defaults: PortalConfig = {
target: 'body',
prepend: false
target: "body",
prepend: false,
};
export const portal: Action<HTMLElement, PortalConfig> = (el, config?: PortalConfig) => {
export const portal: Action<HTMLElement, PortalConfig> = (
el,
config?: PortalConfig,
) => {
let targetEl;
async function update(newConfig: PortalConfig) {
const { target, prepend } = { ...defaults, ...newConfig };
if (typeof target === 'string') {
if (typeof target === "string") {
targetEl = document.querySelector(target);
if (targetEl === null) {
await tick();
@@ -30,11 +33,11 @@ export const portal: Action<HTMLElement, PortalConfig> = (el, config?: PortalCon
} else {
throw new TypeError(
`Unknown portal target type: ${
target === null ? 'null' : typeof target
}. Allowed types: string (CSS selector) or HTMLElement.`
target === null ? "null" : typeof target
}. Allowed types: string (CSS selector) or HTMLElement.`,
);
}
el.dataset.portal = '';
el.dataset.portal = "";
if (prepend) {
targetEl.prepend(el);
} else {
@@ -50,6 +53,6 @@ export const portal: Action<HTMLElement, PortalConfig> = (el, config?: PortalCon
update(config ?? {});
return {
update,
destroy
destroy,
};
};

View File

@@ -1,5 +1,5 @@
import type { Action } from 'svelte/action';
import type { Writable } from 'svelte/store';
import type { Action } from "svelte/action";
import type { Writable } from "svelte/store";
type Args = Writable<DOMRect | null>;
@@ -23,6 +23,6 @@ export const rect: Action<HTMLElement, Args> = (node, store) => {
update,
destroy() {
// no-op
}
},
};
};

View File

@@ -2,15 +2,15 @@ export const scrollToTop = (node: HTMLElement) => {
const handleClick = () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
behavior: "smooth",
});
};
node.addEventListener('click', handleClick);
node.addEventListener("click", handleClick);
return {
destroy() {
node.removeEventListener('click', handleClick);
}
node.removeEventListener("click", handleClick);
},
};
};

View File

@@ -1,5 +1,5 @@
import { isVisible } from '$lib/utils/isVisible';
import type { Action } from 'svelte/action';
import { isVisible } from "$lib/utils/isVisible";
import type { Action } from "svelte/action";
type Args =
| {
@@ -13,7 +13,7 @@ type Args =
export const visible: Action<
HTMLElement,
Args,
{ 'on:visible': (e: CustomEvent<boolean>) => void }
{ "on:visible": (e: CustomEvent<boolean>) => void }
> = (node, args) => {
let visible = false;
@@ -23,11 +23,11 @@ export const visible: Action<
bottom: window.innerHeight,
left: 0,
right: window.innerWidth,
...newArgs
...newArgs,
};
return () => {
visible = isVisible(node, argsWithDefaults);
node.dispatchEvent(new CustomEvent('visible', { detail: visible }));
node.dispatchEvent(new CustomEvent("visible", { detail: visible }));
};
};
@@ -35,15 +35,15 @@ export const visible: Action<
handleVisibility();
function destroy() {
window.removeEventListener('scroll', handleVisibility);
window.removeEventListener('resize', handleVisibility);
window.removeEventListener("scroll", handleVisibility);
window.removeEventListener("resize", handleVisibility);
}
function update(args: Args) {
destroy();
handleVisibility = createVisibilityHandler(args);
window.addEventListener('scroll', handleVisibility);
window.addEventListener('resize', handleVisibility);
window.addEventListener("scroll", handleVisibility);
window.addEventListener("resize", handleVisibility);
}
update(args);
@@ -51,8 +51,8 @@ export const visible: Action<
return {
update,
destroy() {
window.removeEventListener('scroll', handleVisibility);
window.removeEventListener('resize', handleVisibility);
}
window.removeEventListener("scroll", handleVisibility);
window.removeEventListener("resize", handleVisibility);
},
};
};

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { rect } from '$lib/actions';
import { writable } from 'svelte/store';
import { rect } from "$lib/actions";
import { writable } from "svelte/store";
const bodyRect = writable<DOMRect | null>(null);
</script>

View File

@@ -1,10 +1,10 @@
<script lang="ts">
import '$scss/hljs.css';
import "$scss/hljs.css";
import { getCodeHtml } from '$lib/utils/code';
import { getCodeHtml } from "$lib/utils/code";
export let content: string;
$: codeHtml = getCodeHtml({ content, language: 'js' });
$: codeHtml = getCodeHtml({ content, language: "js" });
</script>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->

View File

@@ -1,6 +1,6 @@
<script>
import AutoBox from '../AutoBox.svelte';
import Code from './Code.svelte';
import AutoBox from "../AutoBox.svelte";
import Code from "./Code.svelte";
</script>
<div class="code-console">
@@ -18,7 +18,7 @@
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
@use "$scss/abstract/mixins/border-gradient" as gradients;
.code-console {
@include gradients.border-gradient;
@@ -48,7 +48,11 @@
text-align: left;
border-radius: 12px;
background: linear-gradient(129deg, rgba(0, 0, 0, 0.48) 22.38%, rgba(0, 0, 0, 0) 136.5%);
background: linear-gradient(
129deg,
rgba(0, 0, 0, 0.48) 22.38%,
rgba(0, 0, 0, 0) 136.5%
);
padding: 20px;
position: relative;

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import AutoHeight from '../AutoBox.svelte';
import AutoHeight from "../AutoBox.svelte";
</script>
<div class="anim-box">
@@ -12,7 +12,7 @@
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
@use "$scss/abstract/mixins/border-gradient" as gradients;
.anim-box {
@include gradients.border-gradient;

View File

@@ -1,19 +1,19 @@
<script lang="ts">
import { createCheckbox, melt } from '@melt-ui/svelte';
import { createCheckbox, melt } from "@melt-ui/svelte";
export let checked = false;
const {
elements: { root },
states: { checked: localChecked },
helpers: { isChecked }
helpers: { isChecked },
} = createCheckbox({
onCheckedChange({ next }) {
if (typeof next === 'boolean') {
if (typeof next === "boolean") {
checked = next;
}
return next;
}
},
});
$: localChecked.set(checked);
@@ -43,13 +43,13 @@
position: relative;
&:global(.anim-checkbox[data-state='checked']) {
&:global(.anim-checkbox[data-state="checked"]) {
background-color: #7c67fe;
border-color: #7c67fe;
}
}
[class*='icon-'] {
[class*="icon-"] {
position: absolute;
left: 50%;
top: 50%;

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import Code from '$lib/animations/CodeWindow/Code.svelte';
import { authController } from '.';
import Code from "$lib/animations/CodeWindow/Code.svelte";
import { authController } from ".";
const { state } = authController;

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { Switch } from '$lib/components';
import { objectKeys } from '$lib/utils/object';
import { authController } from '.';
import { Switch } from "$lib/components";
import { objectKeys } from "$lib/utils/object";
import { authController } from ".";
const { state } = authController;
@@ -61,7 +61,7 @@
margin-block: 0.5rem;
}
[class*='icon-'] {
[class*="icon-"] {
--size: 2rem;
font-size: var(--size);
width: var(--size);

View File

@@ -1,18 +1,18 @@
import Box from './box.svelte';
import Code from './code.svelte';
import Controls from './controls.svelte';
import Phone from './phone.svelte';
import Box from "./box.svelte";
import Code from "./code.svelte";
import Controls from "./controls.svelte";
import Phone from "./phone.svelte";
export const Auth = {
Phone,
Box,
Code,
Controls
Controls,
};
import { safeAnimate, sleep, write } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
import { safeAnimate, sleep, write } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { getElSelector } from "../Products.svelte";
type State = {
email: string;
@@ -30,8 +30,8 @@ type State = {
};
const state = createResettable<State>({
email: '',
password: '',
email: "",
password: "",
name: "Walter O'Brian",
showControls: false,
submitted: false,
@@ -39,44 +39,55 @@ const state = createResettable<State>({
GitHub: true,
Google: false,
Apple: false,
Microsoft: false
}
Microsoft: false,
},
});
const emailToSet = 'walterobrian@example.com';
const passwordToSet = 'password';
const emailToSet = "walterobrian@example.com";
const passwordToSet = "password";
const execute = async () => {
const phone = getElSelector('phone');
const box = getElSelector('box');
const code = getElSelector('code');
const controls = getElSelector('controls');
const phone = getElSelector("phone");
const box = getElSelector("box");
const code = getElSelector("code");
const controls = getElSelector("controls");
// Reset
const { update } = state.reset();
await Promise.all([
safeAnimate(box, { x: 310, y: 140, opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(code, { x: 200, y: 460, opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(box, { x: 310, y: 140, opacity: 0 }, { duration: 0.5 })
?.finished,
safeAnimate(code, { x: 200, y: 460, opacity: 0 }, { duration: 0.5 })
?.finished,
safeAnimate(phone, { x: 0, y: 0 }, { duration: 0.5 })?.finished,
safeAnimate(controls, { x: 420, y: 0, opacity: 0 }, { duration: 0.5 })?.finished
safeAnimate(controls, { x: 420, y: 0, opacity: 0 }, { duration: 0.5 })
?.finished,
]);
// Start
await safeAnimate(box, { y: [48, 140], opacity: 1 }, { duration: 0.25, delay: 0.25 })?.finished;
await safeAnimate(
box,
{ y: [48, 140], opacity: 1 },
{ duration: 0.25, delay: 0.25 },
)?.finished;
await sleep(50);
await write(emailToSet, (v) => update((p) => ({ ...p, email: v })), 300);
await sleep(50);
await write(passwordToSet, (v) => update((p) => ({ ...p, password: v })), 300);
await write(
passwordToSet,
(v) => update((p) => ({ ...p, password: v })),
300,
);
await sleep(50);
await safeAnimate(
code,
{ x: [200, 200], y: [460 + 16, 460], opacity: [0, 1] },
{ duration: 0.25 }
{ duration: 0.25 },
)?.finished;
await sleep(350);
@@ -86,10 +97,14 @@ const execute = async () => {
await sleep(1000);
update((p) => ({ ...p, showControls: true }));
safeAnimate(controls, { x: [420, 420], y: [16, 0], opacity: 1 }, { duration: 0.5 });
safeAnimate(
controls,
{ x: [420, 420], y: [16, 0], opacity: 1 },
{ duration: 0.5 },
);
};
export const authController = {
execute,
state
state,
};

View File

@@ -1,12 +1,13 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import { authController } from '.';
import { objectKeys } from '$lib/utils/object';
import { flip } from '$lib/utils/flip';
import { fade } from "svelte/transition";
import { authController } from ".";
import { objectKeys } from "$lib/utils/object";
import { flip } from "$lib/utils/flip";
const { state } = authController;
$: controlsEnabled = $state.showControls && Object.values($state.controls).some(Boolean);
$: controlsEnabled =
$state.showControls && Object.values($state.controls).some(Boolean);
</script>
<div data-theme-ignore class="inner-phone light">
@@ -15,7 +16,12 @@
<div class="inputs">
<fieldset>
<label for="name">Your Name</label>
<input type="name" id="name" placeholder="Enter your name" bind:value={$state.name} />
<input
type="name"
id="name"
placeholder="Enter your name"
bind:value={$state.name}
/>
</fieldset>
<fieldset>
<label for="email">Your Email</label>
@@ -38,7 +44,9 @@
</div>
<button class="sign-up">Sign Up</button>
{#if controlsEnabled}
<span class="with-sep" transition:fade={{ duration: 100 }}>or sign up with</span>
<span class="with-sep" transition:fade={{ duration: 100 }}
>or sign up with</span
>
<div class="oauth-btns" transition:fade={{ duration: 100 }}>
{#each objectKeys($state.controls).filter((p) => $state.controls[p]) as provider (provider)}
<button
@@ -166,7 +174,7 @@
&::before,
&::after {
content: '';
content: "";
height: 1px;
flex-grow: 1;
background-color: hsl(var(--web-color-greyscale-200));
@@ -213,7 +221,8 @@
justify-content: center;
align-items: center;
gap: 0.5rem;
transform: translate(-50%, -50%) scale(var(--inverse-sx, 1), var(--inverse-sy, 1));
transform: translate(-50%, -50%)
scale(var(--inverse-sx, 1), var(--inverse-sy, 1));
}
}
}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import { databasesController } from '.';
import { flip } from '$lib/utils/flip';
import { slide } from "svelte/transition";
import { databasesController } from ".";
import { flip } from "$lib/utils/flip";
const { state } = databasesController;
</script>
@@ -12,7 +12,11 @@
<span class="web-eyebrow">Task</span>
</div>
{#each $state.tasks.slice(0, $state.tableSlice) as task (task.id)}
<div class="row" transition:slide={{ duration: 150 }} animate:flip={{ duration: 150 }}>
<div
class="row"
transition:slide={{ duration: 150 }}
animate:flip={{ duration: 150 }}
>
<div class="copy-button">
<span class="web-icon-copy" />
<span>{task.id}</span>
@@ -34,12 +38,12 @@
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(2.6666667461395264px);
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-600));
}
span:not([class*='icon-']) {
span:not([class*="icon-"]) {
color: var(--greyscale-400, #adadb1);
font-family: Inter;
font-size: 0.875rem;

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import Code from '$lib/animations/CodeWindow/Code.svelte';
import Code from "$lib/animations/CodeWindow/Code.svelte";
let content = `
const result = databases.createDocument(

View File

@@ -1,10 +1,10 @@
import Box from './box.svelte';
import Code from './code.svelte';
import Phone from './phone.svelte';
import Box from "./box.svelte";
import Code from "./code.svelte";
import Phone from "./phone.svelte";
import { safeAnimate, sleep } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
import { safeAnimate, sleep } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { getElSelector } from "../Products.svelte";
type Task = {
id: string;
@@ -20,24 +20,25 @@ type State = {
const state = createResettable<State>({
tasks: [
{
id: '3397fecdedb13397fecdedb1',
title: 'Research user needs',
checked: true
}
id: "3397fecdedb13397fecdedb1",
title: "Research user needs",
checked: true,
},
],
tableSlice: 1
tableSlice: 1,
});
const execute = async () => {
const phone = getElSelector('phone');
const box = getElSelector('box');
const code = getElSelector('code');
const phone = getElSelector("phone");
const box = getElSelector("box");
const code = getElSelector("code");
const { update } = state.reset();
await Promise.all([
safeAnimate(phone, { x: 390, y: 0 }, { duration: 0.5 })?.finished,
safeAnimate(box, { x: 0, y: 32, opacity: 1 }, { duration: 0.5 })?.finished,
safeAnimate(code, { x: 80, y: 320, opacity: 1 }, { duration: 0.5 })?.finished
safeAnimate(code, { x: 80, y: 320, opacity: 1 }, { duration: 0.5 })
?.finished,
]);
await sleep(250);
@@ -47,17 +48,17 @@ const execute = async () => {
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb2',
title: 'Create wireframes',
checked: false
}
]
id: "3397fecdedb13397fecdedb2",
title: "Create wireframes",
checked: false,
},
],
}));
await sleep(250);
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
tableSlice: p.tableSlice + 1,
}));
await sleep(250);
@@ -67,28 +68,28 @@ const execute = async () => {
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb3',
title: 'Create visual design',
checked: false
}
]
id: "3397fecdedb13397fecdedb3",
title: "Create visual design",
checked: false,
},
],
}));
await sleep(250);
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
tableSlice: p.tableSlice + 1,
}));
};
export const databasesController = {
execute,
state
state,
};
export const Databases = {
Phone,
Box,
Code
Code,
};

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import { databasesController } from '.';
import TaskCheckbox from '../TaskCheckbox.svelte';
import { fly } from "svelte/transition";
import { databasesController } from ".";
import TaskCheckbox from "../TaskCheckbox.svelte";
const { state } = databasesController;
</script>
@@ -15,7 +15,11 @@
<div class="date">Today</div>
<div class="tasks">
{#each $state.tasks as task (task.id)}
<div class="task" data-checked={task.checked ? '' : undefined} in:fly={{ x: -16 }}>
<div
class="task"
data-checked={task.checked ? "" : undefined}
in:fly={{ x: -16 }}
>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
@@ -53,7 +57,7 @@
letter-spacing: -0.014rem;
}
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}

View File

@@ -1,8 +1,8 @@
<script lang="ts">
import { portal } from '$lib/actions';
import Code from '$lib/animations/CodeWindow/Code.svelte';
import { fade } from 'svelte/transition';
import { functionsController } from '.';
import { portal } from "$lib/actions";
import Code from "$lib/animations/CodeWindow/Code.svelte";
import { fade } from "svelte/transition";
import { functionsController } from ".";
let content = `
const userId = req.headers['user-id'];
@@ -23,14 +23,14 @@ return res.json({ success: true });`.trim();
<Code {content} />
<div use:portal={{ target: '#code-bottom' }} class="bottom">
{#if $state.submit !== 'idle'}
<div use:portal={{ target: "#code-bottom" }} class="bottom">
{#if $state.submit !== "idle"}
<span class="web-icon-github" in:fade />
{/if}
{#if $state.submit === 'loading'}
{#if $state.submit === "loading"}
<span in:fade>Pushing to GitHub...</span>
<div class="loader is-small" in:fade />
{:else if $state.submit === 'success'}
{:else if $state.submit === "success"}
<span>Deployed to Appwrite Cloud</span>
<span class="web-icon-check" />
{/if}

View File

@@ -1,58 +1,61 @@
import Code from './code.svelte';
import Phone from './phone.svelte';
import Code from "./code.svelte";
import Phone from "./phone.svelte";
import { safeAnimate, sleep } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
import { safeAnimate, sleep } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { getElSelector } from "../Products.svelte";
type State = {
submit: 'idle' | 'loading' | 'success';
submit: "idle" | "loading" | "success";
};
const state = createResettable<State>({
submit: 'idle'
submit: "idle",
});
const execute = async () => {
const phone = getElSelector('phone');
const box = getElSelector('box');
const code = getElSelector('code');
const phone = getElSelector("phone");
const box = getElSelector("box");
const code = getElSelector("code");
const { update } = state.reset();
await Promise.all([
safeAnimate(phone, { x: 430, y: 0, width: '275px' }, { duration: 0.5 })?.finished,
safeAnimate(code, { x: 0, y: 200, opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(box, { opacity: 0 }, { duration: 0.5 })?.finished
safeAnimate(phone, { x: 430, y: 0, width: "275px" }, { duration: 0.5 })
?.finished,
safeAnimate(code, { x: 0, y: 200, opacity: 0 }, { duration: 0.5 })
?.finished,
safeAnimate(box, { opacity: 0 }, { duration: 0.5 })?.finished,
]);
await sleep(250);
await safeAnimate(code, { zIndex: 0 }, { duration: 0 })?.finished;
await safeAnimate(code, { y: [200 - 16, 200], opacity: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(code, { y: [200 - 16, 200], opacity: 1 }, { duration: 0.5 })
?.finished;
await sleep(250);
update((p) => ({
...p,
submit: 'loading'
submit: "loading",
}));
await sleep(1500);
update((p) => ({
...p,
submit: 'success'
submit: "success",
}));
};
export const functionsController = {
execute,
state
state,
};
export const Functions = {
Phone,
Code
Code,
};

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { flip } from '$lib/utils/flip';
import { crossfade, scale, slide } from 'svelte/transition';
import { functionsController } from '.';
import { flip } from "$lib/utils/flip";
import { crossfade, scale, slide } from "svelte/transition";
import { functionsController } from ".";
const { state } = functionsController;
@@ -11,22 +11,22 @@
};
$: methods = [
$state.submit === 'success' && {
icon: '/images/animations/stripe.png',
label: 'Stripe'
$state.submit === "success" && {
icon: "/images/animations/stripe.png",
label: "Stripe",
},
{
icon: '/images/animations/credit-card.svg',
label: 'Card'
icon: "/images/animations/credit-card.svg",
label: "Card",
},
{
icon: '/images/animations/paypal.svg',
label: 'PayPal'
icon: "/images/animations/paypal.svg",
label: "PayPal",
},
{
icon: '/images/animations/apple.svg',
label: 'Apple'
}
icon: "/images/animations/apple.svg",
label: "Apple",
},
].filter(Boolean) as Method[];
</script>
@@ -54,7 +54,7 @@
<li
in:scale={{ delay: 150 }}
animate:flip={{ duration: 500 }}
data-active={i == 0 ? '' : undefined}
data-active={i == 0 ? "" : undefined}
>
<img src={method.icon} alt="" />
<p>{method.label}</p>
@@ -62,7 +62,7 @@
{/each}
</ul>
{#if $state.submit !== 'success'}
{#if $state.submit !== "success"}
<div class="form">
<p>Card information</p>
<div class="bordered">
@@ -81,8 +81,8 @@
<button>
Pay $20.00
{#if $state.submit === 'success'}
<span in:slide={{ axis: 'x' }}>on Stripe</span>
{#if $state.submit === "success"}
<span in:slide={{ axis: "x" }}>on Stripe</span>
{/if}
</button>
</div>
@@ -117,7 +117,7 @@
letter-spacing: -0.014rem;
}
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
@@ -187,7 +187,7 @@
position: relative;
&::before {
content: '';
content: "";
display: block;
position: absolute;
left: 0;
@@ -196,7 +196,7 @@
top: 50%;
transform: translateY(-50%);
background-image: url('/images/animations/check-circle.svg');
background-image: url("/images/animations/check-circle.svg");
}
}
}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { fade, slide } from 'svelte/transition';
import { messagingController } from '.';
import { flip } from '$lib/utils/flip';
import { fade, slide } from "svelte/transition";
import { messagingController } from ".";
import { flip } from "$lib/utils/flip";
const { state } = messagingController;
</script>
@@ -13,7 +13,11 @@
<span class="web-eyebrow" style:text-align="center">Status</span>
</div>
{#each $state.messages.slice(0, $state.tableSlice) as task (task.id)}
<div class="row" transition:slide={{ duration: 150 }} animate:flip={{ duration: 150 }}>
<div
class="row"
transition:slide={{ duration: 150 }}
animate:flip={{ duration: 150 }}
>
<div class="copy-button">
<span class="web-icon-copy" />
<span>{task.id}</span>
@@ -26,7 +30,7 @@
</div>
<div class="status-indicator">
{#if task.status === 'sending'}
{#if task.status === "sending"}
<div class="loader is-small" in:fade />
{:else}
<span class="web-icon-check" />
@@ -55,12 +59,12 @@
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(2.6666667461395264px);
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-600));
}
span:not([class*='icon-']) {
span:not([class*="icon-"]) {
color: var(--greyscale-400, #adadb1);
font-family: Inter;
font-size: 0.875rem;
@@ -90,7 +94,7 @@
align-items: center;
justify-content: center;
}
span:not([class*='icon-']) {
span:not([class*="icon-"]) {
color: var(--greyscale-400, #adadb1);
font-family: Inter;
font-size: 0.875rem;
@@ -108,7 +112,7 @@
align-items: center;
justify-content: center;
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-600));
}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { messagingController } from '.';
import Code from '$lib/animations/CodeWindow/Code.svelte';
import { messagingController } from ".";
import Code from "$lib/animations/CodeWindow/Code.svelte";
const { state } = messagingController;

View File

@@ -1,10 +1,10 @@
import Box from './box.svelte';
import Code from './code.svelte';
import Phone from './phone.svelte';
import Box from "./box.svelte";
import Code from "./code.svelte";
import Phone from "./phone.svelte";
import { safeAnimate, sleep, write } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
import { safeAnimate, sleep, write } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { getElSelector } from "../Products.svelte";
type Task = {
id: string;
@@ -16,7 +16,7 @@ type Message = {
id: string;
type: string;
icon: string;
status: 'sending' | 'sent';
status: "sending" | "sent";
};
type State = {
@@ -25,47 +25,53 @@ type State = {
tasks: Task[];
messages: Message[];
tableSlice: number;
submit: 'loading' | 'success';
submit: "loading" | "success";
};
const state = createResettable<State>({
heading: '',
message: '',
heading: "",
message: "",
tasks: [
{
id: '3397fecdedb13397fecdedb1',
title: 'Research user needs',
checked: true
}
id: "3397fecdedb13397fecdedb1",
title: "Research user needs",
checked: true,
},
],
messages: [
{
id: '...3397fecdedb1',
type: 'SMS',
icon: './images/icons/illustrated/dark/sms.svg',
status: 'sent'
id: "...3397fecdedb1",
type: "SMS",
icon: "./images/icons/illustrated/dark/sms.svg",
status: "sent",
},
{
id: '...2224gabjger4',
type: 'Email',
icon: './images/icons/illustrated/dark/email.svg',
status: 'sent'
}
id: "...2224gabjger4",
type: "Email",
icon: "./images/icons/illustrated/dark/email.svg",
status: "sent",
},
],
tableSlice: 2,
submit: 'loading'
submit: "loading",
});
const execute = async () => {
const phone = getElSelector('phone');
const box = getElSelector('box');
const code = getElSelector('code');
const phone = getElSelector("phone");
const box = getElSelector("box");
const code = getElSelector("code");
const { update } = state.reset();
await Promise.all([
safeAnimate(phone, { x: 365, y: 0, width: '275px' }, { duration: 0.5 })?.finished,
safeAnimate(code, { x: 80, y: 325, opacity: 0, zIndex: 100 }, { duration: 0.5 })?.finished,
safeAnimate(box, { x: 0, y: 32, opacity: 1 }, { duration: 0.5, delay: 1 })?.finished
safeAnimate(phone, { x: 365, y: 0, width: "275px" }, { duration: 0.5 })
?.finished,
safeAnimate(
code,
{ x: 80, y: 325, opacity: 0, zIndex: 100 },
{ duration: 0.5 },
)?.finished,
safeAnimate(box, { x: 0, y: 32, opacity: 1 }, { duration: 0.5, delay: 1 })
?.finished,
]);
await sleep(250);
@@ -75,17 +81,17 @@ const execute = async () => {
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb2',
title: 'Create wireframes',
checked: false
}
]
id: "3397fecdedb13397fecdedb2",
title: "Create wireframes",
checked: false,
},
],
}));
await sleep(250);
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
tableSlice: p.tableSlice + 1,
}));
await sleep(250);
@@ -95,37 +101,38 @@ const execute = async () => {
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb3',
title: 'Create visual design',
checked: false
}
]
id: "3397fecdedb13397fecdedb3",
title: "Create visual design",
checked: false,
},
],
}));
await sleep(250);
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
tableSlice: p.tableSlice + 1,
}));
await sleep(250);
safeAnimate(code, { opacity: 1 }, { duration: 0.5 })?.finished, await sleep(250);
safeAnimate(code, { opacity: 1 }, { duration: 0.5 })?.finished,
await sleep(250);
await write(
'New task assigned to you',
"New task assigned to you",
(v) => {
state.update((n) => ({ ...n, heading: v }));
},
300
300,
);
await write(
'You were assigned a new task in your board. Tap to check it out.',
"You were assigned a new task in your board. Tap to check it out.",
(v) => {
state.update((n) => ({ ...n, message: v }));
},
300
300,
);
await sleep(250);
@@ -135,30 +142,32 @@ const execute = async () => {
messages: [
...p.messages,
{
id: '...5689fdoerre2',
type: 'Push',
icon: './images/icons/illustrated/dark/push.svg',
status: 'sending'
}
]
id: "...5689fdoerre2",
type: "Push",
icon: "./images/icons/illustrated/dark/push.svg",
status: "sending",
},
],
}));
await sleep(1250);
update((p) => ({
...p,
submit: 'success',
messages: p.messages.map((m) => (m.id === '...5689fdoerre2' ? { ...m, status: 'sent' } : m))
submit: "success",
messages: p.messages.map((m) =>
m.id === "...5689fdoerre2" ? { ...m, status: "sent" } : m,
),
}));
};
export const messagingController = {
execute,
state
state,
};
export const Messaging = {
Phone,
Box,
Code
Code,
};

View File

@@ -1,12 +1,12 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import { messagingController } from '.';
import TaskCheckbox from '../TaskCheckbox.svelte';
import { fly } from "svelte/transition";
import { messagingController } from ".";
import TaskCheckbox from "../TaskCheckbox.svelte";
const { state } = messagingController;
</script>
{#if $state.submit === 'success'}
{#if $state.submit === "success"}
<div class="push-notification" in:fly={{ y: -20 }}>
<div class="icon" />
<div class="content">
@@ -14,7 +14,9 @@
<h3 class="title">New task assigned to you</h3>
<span class="time">now</span>
</div>
<p class="message">You were assigned a new task in your board. Tap to check it out.</p>
<p class="message">
You were assigned a new task in your board. Tap to check it out.
</p>
</div>
</div>
{/if}
@@ -28,7 +30,11 @@
<div class="date">Today</div>
<div class="tasks">
{#each $state.tasks as task (task.id)}
<div class="task" data-checked={task.checked ? '' : undefined} in:fly={{ x: -16 }}>
<div
class="task"
data-checked={task.checked ? "" : undefined}
in:fly={{ x: -16 }}
>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
@@ -131,7 +137,7 @@
letter-spacing: -0.014rem;
}
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}

View File

@@ -1,7 +1,7 @@
import { safeAnimate, sleep } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { animate } from 'motion';
import { getElSelector } from '../Products.svelte';
import { safeAnimate, sleep } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { animate } from "motion";
import { getElSelector } from "../Products.svelte";
const requests = createResettable(0);
const databases = createResettable(0);
@@ -12,29 +12,41 @@ const executions = createResettable(0);
const realtime = createResettable(0);
const execute = async () => {
const phone = getElSelector('phone');
const pd = getElSelector('pd');
const phone = getElSelector("phone");
const pd = getElSelector("pd");
const graphBox = getElSelector('graph-box');
const graphBox = getElSelector("graph-box");
const boxesAndStates = [
{ box: getElSelector('post-auth'), state: authentication.reset() },
{ box: getElSelector('post-storage'), state: storage.reset() },
{ box: getElSelector('post-bandwidth'), state: bandwidth.reset() },
{ box: getElSelector('post-functions'), state: executions.reset() },
{ box: getElSelector('post-databases'), state: databases.reset() },
{ box: getElSelector('post-realtime'), state: realtime.reset() },
{ box: getElSelector('post-requests'), state: requests.reset() }
{ box: getElSelector("post-auth"), state: authentication.reset() },
{ box: getElSelector("post-storage"), state: storage.reset() },
{ box: getElSelector("post-bandwidth"), state: bandwidth.reset() },
{ box: getElSelector("post-functions"), state: executions.reset() },
{ box: getElSelector("post-databases"), state: databases.reset() },
{ box: getElSelector("post-realtime"), state: realtime.reset() },
{ box: getElSelector("post-requests"), state: requests.reset() },
];
await Promise.all([
safeAnimate(pd, { opacity: 0, y: -16 }, { duration: 0.5 })?.finished,
safeAnimate(graphBox, { opacity: 0, visibility: 'hidden' }, { duration: 0.5 })?.finished,
safeAnimate(phone, { x: '-50%', width: '660px' }, { duration: 1, delay: 0.5 })?.finished
safeAnimate(
graphBox,
{ opacity: 0, visibility: "hidden" },
{ duration: 0.5 },
)?.finished,
safeAnimate(
phone,
{ x: "-50%", width: "660px" },
{ duration: 1, delay: 0.5 },
)?.finished,
]);
boxesAndStates.forEach(({ box, state }, i) => {
safeAnimate(box, { opacity: 1, y: [1200, 0] }, { duration: 0.5, delay: i * 0.1 })?.finished;
safeAnimate(
box,
{ opacity: 1, y: [1200, 0] },
{ duration: 0.5, delay: i * 0.1 },
)?.finished;
animate(state.set, { duration: 2, delay: (i + 1) * 0.25 });
});
};
@@ -48,6 +60,6 @@ export const postController = {
storage,
bandwidth,
executions,
realtime
}
realtime,
},
};

View File

@@ -1,9 +1,9 @@
import Phone from './phone.svelte';
import Phone from "./phone.svelte";
import { safeAnimate, sleep } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
import { animate } from 'motion';
import { safeAnimate, sleep } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { getElSelector } from "../Products.svelte";
import { animate } from "motion";
type Task = {
title: string;
@@ -29,20 +29,23 @@ const state = createResettable<State>({
tasks: {
todo: [
{
title: 'Edit images for website',
tags: ['design', 'content'],
images: ['./images/animations/storage-2.png', './images/animations/storage-3.png']
}
title: "Edit images for website",
tags: ["design", "content"],
images: [
"./images/animations/storage-2.png",
"./images/animations/storage-3.png",
],
},
],
doing: [
{
title: 'Handoff meet',
tags: ['design', 'dev']
}
],
done: []
title: "Handoff meet",
tags: ["design", "dev"],
},
users: []
],
done: [],
},
users: [],
});
export const connectionsProg = createResettable(0);
@@ -50,47 +53,55 @@ export const connectionsProg = createResettable(0);
const addUser = (update: typeof state.update, user: User) => {
update((p) => ({
...p,
users: [...p.users, user]
users: [...p.users, user],
}));
};
const addTask = (update: typeof state.update, group: keyof State['tasks'], task: Task) => {
const addTask = (
update: typeof state.update,
group: keyof State["tasks"],
task: Task,
) => {
update((p) => ({
...p,
tasks: {
...p.tasks,
[group]: [task, ...p.tasks[group]]
}
[group]: [task, ...p.tasks[group]],
},
}));
};
const execute = async () => {
const phone = getElSelector('phone');
const code = getElSelector('code');
const box = getElSelector('box');
const phone = getElSelector("phone");
const code = getElSelector("code");
const box = getElSelector("box");
const walter = getElSelector('user-Walter');
const aditya = getElSelector('user-Aditya');
const sara = getElSelector('user-Sara');
const walter = getElSelector("user-Walter");
const aditya = getElSelector("user-Aditya");
const sara = getElSelector("user-Sara");
const addTodo = getElSelector('add-todo');
const addDoing = getElSelector('add-doing');
const addDone = getElSelector('add-done');
const addTodo = getElSelector("add-todo");
const addDoing = getElSelector("add-doing");
const addDone = getElSelector("add-done");
const graphBox = getElSelector('graph-box');
const graphBox = getElSelector("graph-box");
const pd = getElSelector('pd');
const pd = getElSelector("pd");
const { update } = state.reset();
const { set: setConn } = connectionsProg.reset();
await Promise.all([
safeAnimate(box, { opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(phone, { x: 0, y: 0, width: '660px' }, { duration: 0.5 })?.finished,
safeAnimate(code, { opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(graphBox, { opacity: 0, x: 0, y: 0, visibility: 'visible' }, { duration: 0 })
safeAnimate(phone, { x: 0, y: 0, width: "660px" }, { duration: 0.5 })
?.finished,
safeAnimate(pd, { opacity: 1, y: 0 }, { duration: 0.5 })?.finished
safeAnimate(code, { opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(
graphBox,
{ opacity: 0, x: 0, y: 0, visibility: "visible" },
{ duration: 0 },
)?.finished,
safeAnimate(pd, { opacity: 1, y: 0 }, { duration: 0.5 })?.finished,
]);
// Graphbox
@@ -101,138 +112,175 @@ const execute = async () => {
(y) => {
setConn(y);
},
{ duration: 2.5, easing: 'ease-in' }
{ duration: 2.5, easing: "ease-in" },
);
});
// Walter
sleep(500).then(async () => {
addUser(update, { name: 'Walter', color: '#fd366e' });
addUser(update, { name: "Walter", color: "#fd366e" });
await sleep(500);
await safeAnimate(walter, { x: -200, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(walter, { x: -200, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await Promise.all([
safeAnimate(walter, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addTodo, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addTodo, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'todo', {
title: 'Handoff meet',
tags: ['design', 'dev']
addTask(update, "todo", {
title: "Handoff meet",
tags: ["design", "dev"],
});
await safeAnimate(walter, { scale: 1, x: -180, y: -160 }, { duration: 0.75, delay: 0.5 })
await safeAnimate(
walter,
{ scale: 1, x: -180, y: -160 },
{ duration: 0.75, delay: 0.5 },
)?.finished;
await sleep(500);
await safeAnimate(walter, { x: 210, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await sleep(500);
await safeAnimate(walter, { x: 210, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await Promise.all([
safeAnimate(walter, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addDone, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addDone, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'done', {
title: 'Create migrations script',
tags: ['Dev']
addTask(update, "done", {
title: "Create migrations script",
tags: ["Dev"],
});
safeAnimate(walter, { scale: 1, x: 230, y: -20 }, { duration: 0.75, delay: 0.5 });
safeAnimate(
walter,
{ scale: 1, x: 230, y: -20 },
{ duration: 0.75, delay: 0.5 },
);
await sleep(750);
await safeAnimate(walter, { x: -10, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(walter, { x: -10, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await Promise.all([
safeAnimate(walter, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addDoing, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addDoing, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'doing', {
title: 'Configure blog SEO',
tags: ['dev', 'content']
addTask(update, "doing", {
title: "Configure blog SEO",
tags: ["dev", "content"],
});
await safeAnimate(walter, { scale: 1, x: -70, y: 80 }, { duration: 0.75, delay: 0.25 });
await safeAnimate(
walter,
{ scale: 1, x: -70, y: 80 },
{ duration: 0.75, delay: 0.25 },
);
});
// Aditya
sleep(1500).then(async () => {
addUser(update, { name: 'Aditya', color: 'rgba(124, 103, 254, 1)' });
addUser(update, { name: "Aditya", color: "rgba(124, 103, 254, 1)" });
await sleep(500);
await safeAnimate(aditya, { x: 200, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(aditya, { x: 200, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await Promise.all([
safeAnimate(aditya, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addDone, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addDone, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'done', {
title: 'Write up briefing',
tags: ['dev-rel']
addTask(update, "done", {
title: "Write up briefing",
tags: ["dev-rel"],
});
await safeAnimate(aditya, { scale: 1, x: 180, y: 60 }, { duration: 0.75, delay: 0.5 })
?.finished;
await safeAnimate(
aditya,
{ scale: 1, x: 180, y: 60 },
{ duration: 0.75, delay: 0.5 },
)?.finished;
await sleep(750);
await safeAnimate(aditya, { x: -210, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(aditya, { x: -210, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await Promise.all([
safeAnimate(aditya, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addTodo, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addTodo, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'todo', {
title: 'Review branding blog post',
tags: ['dev-rel']
addTask(update, "todo", {
title: "Review branding blog post",
tags: ["dev-rel"],
});
await safeAnimate(aditya, { scale: 1, x: 70, y: -220 }, { duration: 0.75, delay: 0.5 })
?.finished;
await safeAnimate(
aditya,
{ scale: 1, x: 70, y: -220 },
{ duration: 0.75, delay: 0.5 },
)?.finished;
});
// Sara
sleep(2500).then(async () => {
addUser(update, { name: 'Sara', color: 'rgba(103, 163, 254, 1)' });
addUser(update, { name: "Sara", color: "rgba(103, 163, 254, 1)" });
await sleep(500);
await safeAnimate(sara, { x: 0, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(sara, { x: 0, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await Promise.all([
safeAnimate(sara, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addDoing, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addDoing, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'doing', {
title: 'Prepare design system presentation',
tags: ['design']
addTask(update, "doing", {
title: "Prepare design system presentation",
tags: ["design"],
});
await safeAnimate(sara, { scale: 1, y: 60, x: -50 }, { duration: 0.75, delay: 0.5 })
?.finished;
await safeAnimate(
sara,
{ scale: 1, y: 60, x: -50 },
{ duration: 0.75, delay: 0.5 },
)?.finished;
await sleep(250);
await safeAnimate(sara, { x: 200, y: -100, scale: 1 }, { duration: 0.5 })?.finished;
await safeAnimate(sara, { x: 200, y: -100, scale: 1 }, { duration: 0.5 })
?.finished;
await Promise.all([
safeAnimate(sara, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished,
safeAnimate(addDone, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished
safeAnimate(addDone, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished,
]);
addTask(update, 'done', {
title: 'QA branding animations',
tags: ['Dev']
addTask(update, "done", {
title: "QA branding animations",
tags: ["Dev"],
});
await safeAnimate(sara, { scale: 1, x: 180, y: 60 }, { duration: 0.75, delay: 0.5 })
?.finished;
await safeAnimate(
sara,
{ scale: 1, x: 180, y: 60 },
{ duration: 0.75, delay: 0.5 },
)?.finished;
});
};
export const realtimeController = {
execute,
state
state,
};
export const Realtime = {
Phone
Phone,
};

View File

@@ -1,10 +1,10 @@
<script lang="ts">
import { objectKeys } from '$lib/utils/object';
import { scale } from 'svelte/transition';
import { connectionsProg, realtimeController } from '.';
import { elId } from '../Products.svelte';
import { flip } from '$lib/utils/flip';
import { toScale } from '$lib/utils/toScale';
import { objectKeys } from "$lib/utils/object";
import { scale } from "svelte/transition";
import { connectionsProg, realtimeController } from ".";
import { elId } from "../Products.svelte";
import { flip } from "$lib/utils/flip";
import { toScale } from "$lib/utils/toScale";
const { state } = realtimeController;
@@ -118,8 +118,18 @@
</div>
{#each $state.users as user}
<div class="user" style:--color={user.color} id="user-{user.name}-{$elId}" in:scale>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<div
class="user"
style:--color={user.color}
id="user-{user.name}-{$elId}"
in:scale
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 18 18"
>
<path
d="M2.58814 0.558469C1.60242 0.224627 1.10955 0.0577053 0.782928 0.173472C0.498743 0.274197 0.275173 0.497766 0.174449 0.781951C0.0586818 1.10858 0.225603 1.60144 0.559445 2.58716L4.67494 14.7388C5.13698 16.1031 5.368 16.7852 5.71194 16.9722C6.00951 17.134 6.36873 17.1341 6.66644 16.9726C7.01055 16.7859 7.24216 16.104 7.70539 14.7402L9.23555 10.235C9.32861 9.96103 9.37513 9.82404 9.45345 9.7101C9.52283 9.60918 9.61015 9.52185 9.71108 9.45248C9.82502 9.37416 9.96201 9.32763 10.236 9.23457L14.7411 7.70441C16.105 7.24118 16.7869 7.00957 16.9736 6.66547C17.1351 6.36776 17.1349 6.00853 16.9732 5.71096C16.7862 5.36702 16.1041 5.136 14.7398 4.67396L2.58814 0.558469Z"
/>
@@ -203,7 +213,14 @@
{@const y = 124 - line}
{#if line > 3}
<circle cx={x} cy={y} r="3" fill="#FD366E" />
<line x1={x} y1={y} x2={x} y2="124" stroke="#FD366E" stroke-width="6" />
<line
x1={x}
y1={y}
x2={x}
y2="124"
stroke="#FD366E"
stroke-width="6"
/>
{/if}
{/each}
</g>
@@ -309,7 +326,7 @@
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
@use "$scss/abstract/mixins/border-gradient" as gradients;
// Utilities
.flow {
@@ -420,7 +437,7 @@
line-height: 1.08563rem; /* 142.857% */
}
[class*='icon'] {
[class*="icon"] {
font-size: 1rem;
}
}
@@ -517,7 +534,7 @@
border-radius: 0.5rem;
border: 1.869px solid var(--greyscale-50, #ededf0);
[class*='icon'] {
[class*="icon"] {
font-size: 1rem;
}
@@ -590,7 +607,7 @@
line-height: 0.875rem; /* 140% */
}
[class*='icon'] {
[class*="icon"] {
font-size: 1rem;
margin-inline-start: auto;
}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import { storageController } from '.';
import { flip } from '$lib/utils/flip';
import { slide } from "svelte/transition";
import { storageController } from ".";
import { flip } from "$lib/utils/flip";
const { state } = storageController;
</script>
@@ -13,7 +13,11 @@
<span class="web-eyebrow">Size</span>
</div>
{#each $state.files as file (file.src)}
<div class="row" in:slide={{ duration: 150 }} animate:flip={{ duration: 150 }}>
<div
class="row"
in:slide={{ duration: 150 }}
animate:flip={{ duration: 150 }}
>
<div class="img-wrapper">
<img src={file.src} alt="" />
<span>{file.filename}</span>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import Code from '$lib/animations/CodeWindow/Code.svelte';
import Code from "$lib/animations/CodeWindow/Code.svelte";
let content = `
const result = storage.createFile(

View File

@@ -1,10 +1,10 @@
import Box from './box.svelte';
import Code from './code.svelte';
import Phone from './phone.svelte';
import Box from "./box.svelte";
import Code from "./code.svelte";
import Phone from "./phone.svelte";
import { safeAnimate, sleep } from '$lib/animations';
import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
import { safeAnimate, sleep } from "$lib/animations";
import { createResettable } from "$lib/utils/resettable";
import { getElSelector } from "../Products.svelte";
type File = {
src: string;
@@ -18,20 +18,20 @@ type State = {
};
const state = createResettable<State>({
files: []
files: [],
});
const execute = async () => {
const phone = getElSelector('phone');
const box = getElSelector('box');
const code = getElSelector('code');
const overlay = getElSelector('overlay');
const drawer = getElSelector('drawer');
const upload = getElSelector('upload');
const uploadBtn = getElSelector('upload-btn');
const uploadImg = getElSelector('upload-img');
const uploadLoading = getElSelector('upload-loading');
const uploadText = getElSelector('upload-text');
const phone = getElSelector("phone");
const box = getElSelector("box");
const code = getElSelector("code");
const overlay = getElSelector("overlay");
const drawer = getElSelector("drawer");
const upload = getElSelector("upload");
const uploadBtn = getElSelector("upload-btn");
const uploadImg = getElSelector("upload-img");
const uploadLoading = getElSelector("upload-loading");
const uploadText = getElSelector("upload-text");
const { update } = state.reset();
@@ -39,7 +39,7 @@ const execute = async () => {
safeAnimate(phone, { x: 0, y: 0 }, { duration: 0.5 })?.finished,
safeAnimate(box, { opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(code, { opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(uploadLoading, { opacity: 0 }, { duration: 0 })?.finished
safeAnimate(uploadLoading, { opacity: 0 }, { duration: 0 })?.finished,
]);
await safeAnimate(code, { zIndex: 20 }, { duration: 0 })?.finished;
@@ -49,30 +49,34 @@ const execute = async () => {
files: [
...p.files,
{
src: '/images/animations/storage-1.png',
filename: 'Profile.png',
type: 'image/png',
size: '362.6 KB'
}
]
src: "/images/animations/storage-1.png",
filename: "Profile.png",
type: "image/png",
size: "362.6 KB",
},
],
}));
await sleep(250);
await Promise.all([
safeAnimate(overlay, { opacity: 1 }, { duration: 0.25 })?.finished,
safeAnimate(drawer, { y: [128, 0], opacity: 1 }, { duration: 0.5 })?.finished
safeAnimate(drawer, { y: [128, 0], opacity: 1 }, { duration: 0.5 })
?.finished,
]);
await sleep(250);
await safeAnimate(uploadBtn, { scale: [1, 0.9, 1] }, { duration: 0.25 })?.finished;
await safeAnimate(uploadBtn, { scale: [1, 0.9, 1] }, { duration: 0.25 })
?.finished;
await safeAnimate(code, { x: 300, y: 32 }, { duration: 0 })?.finished;
await Promise.all([
safeAnimate(code, { y: [32 - 16, 32], opacity: 1 }, { duration: 0.5 })?.finished,
safeAnimate(upload, { y: [-16, 0], opacity: 1 }, { duration: 0.5 })?.finished
safeAnimate(code, { y: [32 - 16, 32], opacity: 1 }, { duration: 0.5 })
?.finished,
safeAnimate(upload, { y: [-16, 0], opacity: 1 }, { duration: 0.5 })
?.finished,
]);
await sleep(250);
@@ -80,9 +84,13 @@ const execute = async () => {
await safeAnimate(box, { x: 300, y: 300 }, { duration: 0 })?.finished;
await Promise.all([
safeAnimate(uploadImg, { x: [64, 48], y: [80, 64], opacity: 1 }, { duration: 0.5 })
safeAnimate(
uploadImg,
{ x: [64, 48], y: [80, 64], opacity: 1 },
{ duration: 0.5 },
)?.finished,
safeAnimate(box, { y: [300 - 16, 300], opacity: 1 }, { duration: 1 })
?.finished,
safeAnimate(box, { y: [300 - 16, 300], opacity: 1 }, { duration: 1 })?.finished
]);
await sleep(250);
@@ -90,7 +98,8 @@ const execute = async () => {
await Promise.all([
safeAnimate(uploadText, { opacity: 0 }, { duration: 0.5 })?.finished,
safeAnimate(uploadLoading, { opacity: 1 }, { duration: 0.5 })?.finished,
safeAnimate(uploadImg, { opacity: 0, y: 64 + 8 }, { duration: 0.5 })?.finished
safeAnimate(uploadImg, { opacity: 0, y: 64 + 8 }, { duration: 0.5 })
?.finished,
]);
await sleep(250);
@@ -101,12 +110,12 @@ const execute = async () => {
files: [
...p.files,
{
src: '/images/animations/storage-2.png',
filename: 'Vector.svg',
type: 'vector/svg',
size: '1.5 KB'
}
]
src: "/images/animations/storage-2.png",
filename: "Vector.svg",
type: "vector/svg",
size: "1.5 KB",
},
],
}));
await sleep(250);
@@ -116,22 +125,22 @@ const execute = async () => {
files: [
...p.files,
{
src: '/images/animations/storage-3.png',
filename: 'img2.webp',
type: 'image/webp',
size: '3.2 MB'
}
]
src: "/images/animations/storage-3.png",
filename: "img2.webp",
type: "image/webp",
size: "3.2 MB",
},
],
}));
};
export const storageController = {
execute,
state
state,
};
export const Storage = {
Phone,
Box,
Code
Code,
};

View File

@@ -1,9 +1,9 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import { storageController } from '.';
import { elId } from '../Products.svelte';
import TaskCheckbox from '../TaskCheckbox.svelte';
import { databasesController } from '../databases';
import { fly } from "svelte/transition";
import { storageController } from ".";
import { elId } from "../Products.svelte";
import TaskCheckbox from "../TaskCheckbox.svelte";
import { databasesController } from "../databases";
const { state: dbState } = databasesController;
const fixedTasks = $dbState.tasks;
@@ -20,7 +20,11 @@
<div class="date">Today</div>
<div class="tasks">
{#each fixedTasks as task (task.id)}
<div class="task" data-checked={task.checked ? '' : undefined} in:fly={{ x: -16 }}>
<div
class="task"
data-checked={task.checked ? "" : undefined}
in:fly={{ x: -16 }}
>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
@@ -83,7 +87,7 @@
letter-spacing: -0.014rem;
}
[class*='icon-'] {
[class*="icon-"] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}

View File

@@ -1,16 +1,16 @@
import type { Action } from 'svelte/action';
import type { Action } from "svelte/action";
import {
animate as motionAnimate,
type ElementOrSelector,
type MotionKeyframesDefinition,
type AnimationOptionsWithOverrides,
animate
} from 'motion';
animate,
} from "motion";
export function animation(
elementOrSelector: ElementOrSelector,
keyframes: MotionKeyframesDefinition,
options?: AnimationOptionsWithOverrides
options?: AnimationOptionsWithOverrides,
) {
const play = () => {
const played = motionAnimate(elementOrSelector, keyframes, options);
@@ -20,16 +20,23 @@ export function animation(
const reverse = () => {
const reversedKeyframes = Object.fromEntries(
Object.entries(keyframes).map(([key, keyframe]) => {
return [key, Array.isArray(keyframe) ? [...keyframe].reverse() : keyframe];
})
return [
key,
Array.isArray(keyframe) ? [...keyframe].reverse() : keyframe,
];
}),
) as typeof keyframes;
const reversed = motionAnimate(elementOrSelector, reversedKeyframes, options);
const reversed = motionAnimate(
elementOrSelector,
reversedKeyframes,
options,
);
return reversed;
};
return {
play,
reverse
reverse,
};
}
@@ -38,7 +45,7 @@ export type Animation = ReturnType<typeof animation>;
export const safeAnimate = (
elementOrSelector: ElementOrSelector,
keyframes: MotionKeyframesDefinition,
options?: AnimationOptionsWithOverrides
options?: AnimationOptionsWithOverrides,
) => {
try {
return animate(elementOrSelector, keyframes, options);
@@ -49,7 +56,7 @@ export const safeAnimate = (
type Unsubscriber = () => void;
type PreviousScroll = 'before' | 'after' | undefined;
type PreviousScroll = "before" | "after" | undefined;
type ScrollCallbackState = {
previous?: PreviousScroll;
@@ -59,28 +66,33 @@ type ScrollCallbackState = {
export type ScrollCallback = {
percentage: number;
whenAfter?: (args: Omit<ScrollCallbackState, 'unsubscribe'>) => Unsubscriber | void;
whenAfter?: (
args: Omit<ScrollCallbackState, "unsubscribe">,
) => Unsubscriber | void;
};
export function createScrollHandler(callbacks: ScrollCallback[]) {
const states: ScrollCallbackState[] = callbacks.map(() => ({ executedCount: 0 }));
const states: ScrollCallbackState[] = callbacks.map(() => ({
executedCount: 0,
}));
const handler = function (scrollPercentage: number) {
callbacks.forEach((callback, i) => {
const { percentage, whenAfter } = callback;
const { previous, unsubscribe, executedCount } = states[i];
if (scrollPercentage >= percentage && previous !== 'after') {
if (scrollPercentage >= percentage && previous !== "after") {
// Execute whenAfter
states[i].unsubscribe = whenAfter?.({ previous, executedCount }) ?? undefined;
states[i].previous = 'after';
states[i].unsubscribe =
whenAfter?.({ previous, executedCount }) ?? undefined;
states[i].previous = "after";
if (whenAfter) {
states[i].executedCount++;
}
} else if (scrollPercentage < percentage && previous === 'after') {
} else if (scrollPercentage < percentage && previous === "after") {
unsubscribe?.();
states[i].unsubscribe = undefined;
states[i].previous = 'before';
states[i].previous = "before";
}
});
};
@@ -107,8 +119,8 @@ export const scroll: Action<
HTMLElement,
undefined,
{
'on:web-scroll': (e: CustomEvent<ScrollInfo>) => void;
'on:web-resize': (e: CustomEvent<ScrollInfo>) => void;
"on:web-scroll": (e: CustomEvent<ScrollInfo>) => void;
"on:web-resize": (e: CustomEvent<ScrollInfo>) => void;
}
> = (node) => {
function getScrollInfo(): ScrollInfo {
@@ -124,34 +136,34 @@ export const scroll: Action<
return {
percentage: scrollPercentage,
traversed,
remaning
remaning,
};
}
const createHandler = (eventName: 'web-scroll' | 'web-resize') => {
const createHandler = (eventName: "web-scroll" | "web-resize") => {
return () => {
node.dispatchEvent(
new CustomEvent<ScrollInfo>(eventName, {
detail: getScrollInfo()
})
detail: getScrollInfo(),
}),
);
};
};
const handleScroll = createHandler('web-scroll');
const handleResize = createHandler('web-resize');
const handleScroll = createHandler("web-scroll");
const handleResize = createHandler("web-resize");
handleScroll();
handleResize();
window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleResize);
window.addEventListener("scroll", handleScroll);
window.addEventListener("resize", handleResize);
return {
destroy() {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleResize);
}
window.removeEventListener("scroll", handleScroll);
window.removeEventListener("resize", handleResize);
},
};
};
@@ -197,7 +209,9 @@ export function createProgressSequence(events: ProgressEvent[]) {
let lastEventIdx = -1;
const handler = (percentage: number) => {
const idx = sortedEvents.findIndex((event) => event.percentage <= percentage);
const idx = sortedEvents.findIndex(
(event) => event.percentage <= percentage,
);
if (idx === lastEventIdx) {
return;
}
@@ -237,8 +251,8 @@ export function sleep(duration: number) {
export function getInitials(name: string) {
return name
.split(' ')
.map((word) => word?.[0]?.toUpperCase() ?? '')
.join('')
.split(" ")
.map((word) => word?.[0]?.toUpperCase() ?? "")
.join("")
.slice(0, 2);
}

View File

@@ -1,9 +1,14 @@
import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_ID } from '$env/static/public';
import { Client, Databases, Functions } from '@appwrite.io/console';
import {
PUBLIC_APPWRITE_ENDPOINT,
PUBLIC_APPWRITE_PROJECT_ID,
} from "$env/static/public";
import { Client, Databases, Functions } from "@appwrite.io/console";
export const client = new Client();
client.setEndpoint(PUBLIC_APPWRITE_ENDPOINT).setProject(PUBLIC_APPWRITE_PROJECT_ID);
client
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT_ID);
export const databases = new Databases(client);
export const functions = new Functions(client);

View File

@@ -1,6 +1,9 @@
import { APPWRITE_API_KEY_INIT } from '$env/static/private';
import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_INIT_ID } from '$env/static/public';
import { Account, Client, Databases } from '@appwrite.io/console';
import { APPWRITE_API_KEY_INIT } from "$env/static/private";
import {
PUBLIC_APPWRITE_ENDPOINT,
PUBLIC_APPWRITE_PROJECT_INIT_ID,
} from "$env/static/public";
import { Account, Client, Databases } from "@appwrite.io/console";
const clientServer = new Client();
clientServer
@@ -10,5 +13,5 @@ clientServer
export const appwriteInitServer = {
account: new Account(clientServer),
databases: new Databases(clientServer)
databases: new Databases(clientServer),
};

View File

@@ -1,10 +1,15 @@
import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_INIT_ID } from '$env/static/public';
import { Client, Account } from '@appwrite.io/console';
import {
PUBLIC_APPWRITE_ENDPOINT,
PUBLIC_APPWRITE_PROJECT_INIT_ID,
} from "$env/static/public";
import { Client, Account } from "@appwrite.io/console";
const client = new Client();
client.setEndpoint(PUBLIC_APPWRITE_ENDPOINT).setProject(PUBLIC_APPWRITE_PROJECT_INIT_ID);
client
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT_INIT_ID);
export const appwriteInit = {
client,
account: new Account(client)
account: new Account(client),
};

View File

@@ -1,4 +1,4 @@
import Root from './Root.svelte';
import Item from './Item.svelte';
import Root from "./Root.svelte";
import Item from "./Item.svelte";
export { Root as Accordion, Item as AccordionItem };

View File

@@ -1,10 +1,10 @@
<script lang="ts">
import { browser } from '$app/environment';
import { BANNER_KEY } from '$lib/constants';
import { browser } from "$app/environment";
import { BANNER_KEY } from "$lib/constants";
const hideTopBanner = () => {
document.body.dataset.bannerHidden = '';
localStorage.setItem(BANNER_KEY, 'true');
document.body.dataset.bannerHidden = "";
localStorage.setItem(BANNER_KEY, "true");
};
</script>

View File

@@ -1,21 +1,21 @@
<script lang="ts">
import { DropdownCtx } from './DropdownMenu.svelte';
import { DropdownCtx } from "./DropdownMenu.svelte";
export let checked = false;
const {
builders: { createCheckboxItem }
builders: { createCheckboxItem },
} = DropdownCtx.get();
const {
elements: { checkboxItem },
states: { checked: localChecked }
states: { checked: localChecked },
} = createCheckboxItem({
defaultChecked: checked,
onCheckedChange({ next }) {
checked = !!next;
return next;
}
},
});
$: localChecked.set(checked);

View File

@@ -1,5 +1,5 @@
<script lang="ts" context="module">
const ctxKey = Symbol('dropdown');
const ctxKey = Symbol("dropdown");
export const DropdownCtx = {
get() {
@@ -7,26 +7,26 @@
},
set(ctx: DropdownMenu) {
setContext(ctxKey, ctx);
}
},
};
</script>
<script lang="ts">
import { createDropdownMenu, type DropdownMenu } from '@melt-ui/svelte';
import { getContext, setContext } from 'svelte';
import { createDropdownMenu, type DropdownMenu } from "@melt-ui/svelte";
import { getContext, setContext } from "svelte";
const dropdown = createDropdownMenu({
forceVisible: true,
positioning: {
placement: 'bottom-start'
}
placement: "bottom-start",
},
});
DropdownCtx.set(dropdown);
const {
elements: { menu, trigger },
states: { open }
states: { open },
} = dropdown;
</script>

View File

@@ -1,18 +1,22 @@
<script lang="ts">
export let src: string;
export let alt = '';
export let alt = "";
export let size: number;
let className = '';
let className = "";
export { className as class };
</script>
<div class="head-wrapper {className}" style:width="{size}px" style:height="{size}px">
<div
class="head-wrapper {className}"
style:width="{size}px"
style:height="{size}px"
>
<img {src} {alt} />
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
@use "$scss/abstract/mixins/border-gradient" as gradients;
.head-wrapper {
@include gradients.border-gradient;

View File

@@ -1,13 +1,13 @@
<script lang="ts">
import { clamp } from '$lib/utils/clamp';
import FloatingHead from './FloatingHead.svelte';
import { clamp } from "$lib/utils/clamp";
import FloatingHead from "./FloatingHead.svelte";
type Head = {
src: string;
display: [number, number, number];
};
const headPositions: Array<Head['display']> = [
const headPositions: Array<Head["display"]> = [
[120, -25, 40],
[120, 20, -40],
[64, -40, 20],
@@ -17,13 +17,13 @@
[64, 40, -20],
[64, 10, -55],
[40, -45, 50],
[40, -40, -40]
[40, -40, -40],
];
export let images: Array<string>;
</script>
<div class="absolute web-u-hide-mobile root">
<div class="web-u-hide-mobile root absolute">
{#each headPositions as [size, top, left], i}
{@const image = clamp(0, images.length - 1, i % images.length)}
<FloatingHead

View File

@@ -1,64 +1,66 @@
<script lang="ts">
import { createAccordion, melt } from '@melt-ui/svelte';
import { slide } from 'svelte/transition';
import { createAccordion, melt } from "@melt-ui/svelte";
import { slide } from "svelte/transition";
export let noBorder = false;
const {
elements: { content, heading, item, root, trigger },
helpers: { isSelected }
helpers: { isSelected },
} = createAccordion({
multiple: true,
forceVisible: true
forceVisible: true,
});
const links: Record<string, { label: string; href: string; target?: string; rel?: string }[]> =
{
'Quick starts': [
{ label: 'Web', href: '/docs/quick-starts/web' },
{ label: 'Next.js', href: '/docs/quick-starts/nextjs' },
{ label: 'React', href: '/docs/quick-starts/react' },
{ label: 'Vue.js', href: '/docs/quick-starts/vue' },
{ label: 'Nuxt', href: '/docs/quick-starts/nuxt' },
{ label: 'SvelteKit', href: '/docs/quick-starts/sveltekit' },
{ label: 'Refine', href: '/docs/quick-starts/refine' },
{ label: 'Angular', href: '/docs/quick-starts/angular' },
{ label: 'React Native', href: '/docs/quick-starts/react-native' },
{ label: 'Flutter', href: '/docs/quick-starts/flutter' },
{ label: 'Apple', href: '/docs/quick-starts/apple' },
{ label: 'Android', href: '/docs/quick-starts/android' },
{ label: 'Qwik', href: '/docs/quick-starts/qwik' },
{ label: 'Astro', href: '/docs/quick-starts/astro' },
{ label: 'Solid', href: '/docs/quick-starts/solid' }
const links: Record<
string,
{ label: string; href: string; target?: string; rel?: string }[]
> = {
"Quick starts": [
{ label: "Web", href: "/docs/quick-starts/web" },
{ label: "Next.js", href: "/docs/quick-starts/nextjs" },
{ label: "React", href: "/docs/quick-starts/react" },
{ label: "Vue.js", href: "/docs/quick-starts/vue" },
{ label: "Nuxt", href: "/docs/quick-starts/nuxt" },
{ label: "SvelteKit", href: "/docs/quick-starts/sveltekit" },
{ label: "Refine", href: "/docs/quick-starts/refine" },
{ label: "Angular", href: "/docs/quick-starts/angular" },
{ label: "React Native", href: "/docs/quick-starts/react-native" },
{ label: "Flutter", href: "/docs/quick-starts/flutter" },
{ label: "Apple", href: "/docs/quick-starts/apple" },
{ label: "Android", href: "/docs/quick-starts/android" },
{ label: "Qwik", href: "/docs/quick-starts/qwik" },
{ label: "Astro", href: "/docs/quick-starts/astro" },
{ label: "Solid", href: "/docs/quick-starts/solid" },
],
Products: [
{ label: 'Auth', href: '/docs/products/auth' },
{ label: 'Databases', href: '/docs/products/databases' },
{ label: 'Functions', href: '/docs/products/functions' },
{ label: 'Messaging', href: '/products/messaging' },
{ label: 'Storage', href: '/docs/products/storage' },
{ label: 'Realtime', href: '/docs/apis/realtime' }
{ label: "Auth", href: "/docs/products/auth" },
{ label: "Databases", href: "/docs/products/databases" },
{ label: "Functions", href: "/docs/products/functions" },
{ label: "Messaging", href: "/products/messaging" },
{ label: "Storage", href: "/docs/products/storage" },
{ label: "Realtime", href: "/docs/apis/realtime" },
],
Learn: [
{ label: 'Docs', href: '/docs' },
{ label: 'Integrations', href: '/integrations' },
{ label: 'Community', href: '/community' },
{ label: 'Init', href: '/init' },
{ label: 'Threads', href: '/threads' },
{ label: 'Blog', href: '/blog' },
{ label: 'Changelog', href: '/changelog' },
{ label: "Docs", href: "/docs" },
{ label: "Integrations", href: "/integrations" },
{ label: "Community", href: "/community" },
{ label: "Init", href: "/init" },
{ label: "Threads", href: "/threads" },
{ label: "Blog", href: "/blog" },
{ label: "Changelog", href: "/changelog" },
{
label: 'Roadmap',
href: 'https://github.com/orgs/appwrite/projects',
target: '_blank',
rel: 'noopener noreferrer'
label: "Roadmap",
href: "https://github.com/orgs/appwrite/projects",
target: "_blank",
rel: "noopener noreferrer",
},
{
label: 'Source code',
href: 'https://github.com/appwrite',
target: '_blank',
rel: 'noopener noreferrer'
}
label: "Source code",
href: "https://github.com/appwrite",
target: "_blank",
rel: "noopener noreferrer",
},
// {
// label: 'Status',
// href: 'https://appwrite.online',
@@ -67,27 +69,27 @@
// }
],
Programs: [
{ label: 'Heroes', href: '/heroes' },
{ label: 'Startups', href: '/startups' }
{ label: "Heroes", href: "/heroes" },
{ label: "Startups", href: "/startups" },
],
About: [
{ label: 'Company', href: '/company' },
{ label: 'Pricing', href: '/pricing' },
{ label: "Company", href: "/company" },
{ label: "Pricing", href: "/pricing" },
{
label: 'Careers',
href: 'https://appwrite.careers',
target: '_blank',
rel: 'noopener noreferrer'
label: "Careers",
href: "https://appwrite.careers",
target: "_blank",
rel: "noopener noreferrer",
},
{
label: 'Store',
href: 'https://appwrite.store',
target: '_blank',
rel: 'noopener noreferrer'
label: "Store",
href: "https://appwrite.store",
target: "_blank",
rel: "noopener noreferrer",
},
{ label: 'Contact us', href: '/contact-us' },
{ label: 'Assets', href: '/assets' }
]
{ label: "Contact us", href: "/contact-us" },
{ label: "Assets", href: "/assets" },
],
};
</script>
@@ -96,11 +98,19 @@
class="web-footer-nav relative mt-24"
class:web-u-sep-block-start={!noBorder}
>
<img class="web-logo" src="/images/logos/appwrite.svg" alt="appwrite" height="24" width="130" />
<img
class="web-logo"
src="/images/logos/appwrite.svg"
alt="appwrite"
height="24"
width="130"
/>
<ul class="web-footer-nav-main-list" use:melt={$root}>
{#each Object.entries(links) as [title, items]}
<li class="web-footer-nav-main-item web-is-not-mobile">
<h2 class="web-footer-nav-main-title web-is-not-mobile web-caption-500 web-eyebrow">
<h2
class="web-footer-nav-main-title web-is-not-mobile web-caption-500 web-eyebrow"
>
{title}
</h2>
<ul class="web-footer-nav-secondary-list web-sub-body-400">

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import { onMount } from 'svelte';
import { page } from "$app/stores";
import { onMount } from "svelte";
let mounted = false;
@@ -28,7 +28,7 @@
const randomDelay = () => Math.floor(Math.random() * 750);
</script>
<div class="banner" class:hidden={$page.url.pathname.includes('init')}>
<div class="banner" class:hidden={$page.url.pathname.includes("init")}>
<div class="content web-u-color-text-primary">
<div class="headings">
<span style:font-weight="500"
@@ -235,7 +235,7 @@
}
&::after {
content: '';
content: "";
display: block;
position: absolute;
transform-origin: left;

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { buildOpenGraphImage } from '$lib/utils/metadata';
import { buildOpenGraphImage } from "$lib/utils/metadata";
export let title: string;
export let description: string;

View File

@@ -1,63 +1,63 @@
<script lang="ts">
import Tooltip from '$lib/components/Tooltip.svelte';
import { themeInUse } from '$routes/+layout.svelte';
import Tooltip from "$lib/components/Tooltip.svelte";
import { themeInUse } from "$routes/+layout.svelte";
$: platforms = [
{
name: 'Flutter',
href: '/docs/quick-starts/flutter',
image: `/images/platforms/${$themeInUse}/flutter.svg`
name: "Flutter",
href: "/docs/quick-starts/flutter",
image: `/images/platforms/${$themeInUse}/flutter.svg`,
},
{
name: 'Next',
href: '/docs/quick-starts/nextjs',
image: `/images/platforms/${$themeInUse}/nextjs.svg`
name: "Next",
href: "/docs/quick-starts/nextjs",
image: `/images/platforms/${$themeInUse}/nextjs.svg`,
},
{
name: 'React',
href: '/docs/quick-starts/react',
image: `/images/platforms/${$themeInUse}/react.svg`
name: "React",
href: "/docs/quick-starts/react",
image: `/images/platforms/${$themeInUse}/react.svg`,
},
{
name: 'Svelte',
href: '/docs/quick-starts/sveltekit',
image: `/images/platforms/${$themeInUse}/svelte.svg`
name: "Svelte",
href: "/docs/quick-starts/sveltekit",
image: `/images/platforms/${$themeInUse}/svelte.svg`,
},
{
name: 'Nuxt',
href: '/docs/quick-starts/nuxt',
image: `/images/platforms/${$themeInUse}/nuxt.svg`
name: "Nuxt",
href: "/docs/quick-starts/nuxt",
image: `/images/platforms/${$themeInUse}/nuxt.svg`,
},
{
name: 'Vue',
href: '/docs/quick-starts/vue',
image: `/images/platforms/${$themeInUse}/vue.svg`
name: "Vue",
href: "/docs/quick-starts/vue",
image: `/images/platforms/${$themeInUse}/vue.svg`,
},
{
name: 'Angular',
href: '/docs/quick-starts/angular',
image: `/images/platforms/${$themeInUse}/angular.svg`
name: "Angular",
href: "/docs/quick-starts/angular",
image: `/images/platforms/${$themeInUse}/angular.svg`,
},
{
name: 'Refine',
href: '/docs/quick-starts/refine',
image: `/images/platforms/${$themeInUse}/refine.svg`
name: "Refine",
href: "/docs/quick-starts/refine",
image: `/images/platforms/${$themeInUse}/refine.svg`,
},
{
name: 'Apple',
href: '/docs/quick-starts/apple',
image: `/images/platforms/${$themeInUse}/apple.svg`
name: "Apple",
href: "/docs/quick-starts/apple",
image: `/images/platforms/${$themeInUse}/apple.svg`,
},
{
name: 'Android',
href: '/docs/quick-starts/android',
image: `/images/platforms/${$themeInUse}/android.svg`
name: "Android",
href: "/docs/quick-starts/android",
image: `/images/platforms/${$themeInUse}/android.svg`,
},
{
name: 'React Native',
href: '/docs/quick-starts/react-native',
image: `/images/platforms/${$themeInUse}/react-native.svg`
}
name: "React Native",
href: "/docs/quick-starts/react-native",
image: `/images/platforms/${$themeInUse}/react-native.svg`,
},
] as Array<{
name: string;
href: string;
@@ -65,11 +65,16 @@
}>;
</script>
<ul class="flex flex-wrap gap-4 web-u-margin-block-32-mobile web-u-margin-block-40-not-mobile">
<ul
class="web-u-margin-block-32-mobile web-u-margin-block-40-not-mobile flex flex-wrap gap-4"
>
{#each platforms as platform}
<Tooltip>
<li>
<a href={platform.href} class="web-icon-button web-box-icon has-border-gradient">
<a
href={platform.href}
class="web-icon-button web-box-icon has-border-gradient"
>
<img
src={platform.image}
alt="{platform.name} quick start"

View File

@@ -1,24 +1,24 @@
<script lang="ts">
import { type Theme, currentTheme } from '$routes/+layout.svelte';
import { type Theme, currentTheme } from "$routes/+layout.svelte";
import Select, { type SelectOption } from './Select.svelte';
import Select, { type SelectOption } from "./Select.svelte";
const options: SelectOption<Theme>[] = [
{
value: 'dark',
label: 'Dark',
icon: 'web-icon-dark'
value: "dark",
label: "Dark",
icon: "web-icon-dark",
},
{
value: 'light',
label: 'Light',
icon: 'web-icon-light'
value: "light",
label: "Light",
icon: "web-icon-light",
},
{
value: 'system',
label: 'System',
icon: 'web-icon-system'
}
value: "system",
label: "System",
icon: "web-icon-system",
},
];
</script>

View File

@@ -1,14 +1,14 @@
<script lang="ts">
import { getTocCtx } from './TocRoot.svelte';
import TocTree from './TocTree.svelte';
import { getTocCtx } from "./TocRoot.svelte";
import TocTree from "./TocTree.svelte";
export let showToc = true;
const {
toc: {
elements: { item },
states: { activeHeadingIdxs, headingsTree }
}
states: { activeHeadingIdxs, headingsTree },
},
} = getTocCtx();
$: progress = Math.max(...$activeHeadingIdxs) / ($headingsTree.length - 1);
@@ -100,6 +100,10 @@
</svg>
</span>
</div>
<TocTree tree={$headingsTree} activeHeadingIdxs={$activeHeadingIdxs} {item} />
<TocTree
tree={$headingsTree}
activeHeadingIdxs={$activeHeadingIdxs}
{item}
/>
</div>
</aside>

View File

@@ -2,9 +2,9 @@
import {
createTableOfContents,
type CreateTableOfContentsArgs,
type TableOfContents
} from '@melt-ui/svelte';
import { getContext, setContext } from 'svelte';
type TableOfContents,
} from "@melt-ui/svelte";
import { getContext, setContext } from "svelte";
const TOC_KEY = Symbol();
export type TocContext = {
@@ -21,16 +21,22 @@
</script>
<script lang="ts">
export let selector = '#main';
export let exclude: CreateTableOfContentsArgs['exclude'] = ['h1', 'h3', 'h4', 'h5', 'h6'];
export let activeType: CreateTableOfContentsArgs['activeType'] = 'highest';
export let scrollOffset: CreateTableOfContentsArgs['scrollOffset'] = 0;
export let selector = "#main";
export let exclude: CreateTableOfContentsArgs["exclude"] = [
"h1",
"h3",
"h4",
"h5",
"h6",
];
export let activeType: CreateTableOfContentsArgs["activeType"] = "highest";
export let scrollOffset: CreateTableOfContentsArgs["scrollOffset"] = 0;
const toc = createTableOfContents({
selector,
exclude,
activeType,
scrollOffset
scrollOffset,
});
setCtx({ toc });

View File

@@ -1,16 +1,20 @@
<script lang="ts">
import { type TableOfContentsItem, type TableOfContentsElements, melt } from '@melt-ui/svelte';
import { getTocCtx } from './TocRoot.svelte';
import {
type TableOfContentsItem,
type TableOfContentsElements,
melt,
} from "@melt-ui/svelte";
import { getTocCtx } from "./TocRoot.svelte";
export let tree: TableOfContentsItem[] = [];
export let activeHeadingIdxs: number[];
export let item: TableOfContentsElements['item'];
export let item: TableOfContentsElements["item"];
export let level = 1;
const {
toc: {
helpers: { isActive }
}
helpers: { isActive },
},
} = getTocCtx();
</script>

View File

@@ -1,51 +1,51 @@
<script lang="ts">
import { createTooltip, melt } from '@melt-ui/svelte';
import type { FloatingConfig } from '@melt-ui/svelte/internal/actions';
import { fly, type FlyParams } from 'svelte/transition';
import { createTooltip, melt } from "@melt-ui/svelte";
import type { FloatingConfig } from "@melt-ui/svelte/internal/actions";
import { fly, type FlyParams } from "svelte/transition";
export let placement: NonNullable<FloatingConfig>['placement'] = 'top';
export let placement: NonNullable<FloatingConfig>["placement"] = "top";
export let disabled = false;
export let closeOnPointerDown = false;
export let disableHoverableContent = false;
const {
elements: { trigger, content, arrow },
states: { open }
states: { open },
} = createTooltip({
positioning: {
placement
placement,
},
openDelay: 0,
closeOnPointerDown,
forceVisible: true,
disableHoverableContent
disableHoverableContent,
});
$: flyParams = (function getFlyParams() {
const params: FlyParams = {
duration: 150
duration: 150,
};
switch (placement) {
case 'top':
case 'top-start':
case 'top-end':
case "top":
case "top-start":
case "top-end":
params.y = 4;
break;
case 'bottom':
case 'bottom-start':
case 'bottom-end':
case "bottom":
case "bottom-start":
case "bottom-end":
params.y = -4;
break;
case 'left':
case 'left-start':
case 'left-end':
case "left":
case "left-start":
case "left-end":
params.x = 4;
break;
case 'right':
case 'right-start':
case 'right-end':
case "right":
case "right-start":
case "right-end":
params.x = -4;
break;
}
@@ -63,7 +63,11 @@
{/if}
{#if $open && !disabled}
<div use:melt={$content} class="web-tooltip web-sub-body-400" transition:fly={flyParams}>
<div
use:melt={$content}
class="web-tooltip web-sub-body-400"
transition:fly={flyParams}
>
<div use:melt={$arrow} />
<slot name="tooltip" />
</div>

View File

@@ -5,7 +5,7 @@
</li>
<style lang="scss">
@use '$scss/abstract' as *;
@use "$scss/abstract" as *;
.slide {
cursor: grab;

View File

@@ -1,2 +1,2 @@
export { default as Root } from './Carousel.svelte';
export { default as Slide } from './CarouselSlide.svelte';
export { default as Root } from "./Carousel.svelte";
export { default as Slide } from "./CarouselSlide.svelte";

View File

@@ -1,7 +1,7 @@
export const GITHUB_STARS = '42.8K';
export const BANNER_KEY: Banners = 'discord-banner-01'; // Change key to force banner to show again
export const GITHUB_STARS = "42.8K";
export const BANNER_KEY: Banners = "discord-banner-01"; // Change key to force banner to show again
export const SENTRY_DSN =
'https://27d41dc8bb67b596f137924ab8599e59@o1063647.ingest.us.sentry.io/4507497727000576';
"https://27d41dc8bb67b596f137924ab8599e59@o1063647.ingest.us.sentry.io/4507497727000576";
/**
* History:
@@ -9,8 +9,7 @@ export const SENTRY_DSN =
* init-banner-02
* pricing-banner-01
*/
type Banners = 'discord-banner-01' | 'init-banner-02' | 'pricing-banner-01'
type Banners = "discord-banner-01" | "init-banner-02" | "pricing-banner-01";
export type Social = {
icon: string;
@@ -22,60 +21,60 @@ export type SocialShareOption = {
icon: string;
label: string;
link: string;
type: 'link' | 'copy';
type: "link" | "copy";
};
export const socialSharingOptions: Array<SocialShareOption> = [
{
icon: 'web-icon-x',
label: 'Twitter',
link: 'https://x.com/intent/post?text={TITLE}\n&url={URL}',
type: 'link'
icon: "web-icon-x",
label: "Twitter",
link: "https://x.com/intent/post?text={TITLE}\n&url={URL}",
type: "link",
},
{
icon: 'web-icon-linkedin',
label: 'LinkedIn',
link: 'https://www.linkedin.com/sharing/share-offsite?text={TITLE}\n&url={URL}',
type: 'link'
icon: "web-icon-linkedin",
label: "LinkedIn",
link: "https://www.linkedin.com/sharing/share-offsite?text={TITLE}\n&url={URL}",
type: "link",
},
{
icon: 'web-icon-ycombinator',
label: 'YCombinator',
link: 'https://news.ycombinator.com/submitlink?t={TITLE}\n&u={URL}',
type: 'link'
icon: "web-icon-ycombinator",
label: "YCombinator",
link: "https://news.ycombinator.com/submitlink?t={TITLE}\n&u={URL}",
type: "link",
},
{
icon: 'web-icon-copy',
label: 'Copy',
link: '',
type: 'copy'
}
]
icon: "web-icon-copy",
label: "Copy",
link: "",
type: "copy",
},
];
export const socials: Array<Social> = [
{
icon: 'web-icon-discord',
label: 'Discord',
link: 'https://appwrite.io/discord'
icon: "web-icon-discord",
label: "Discord",
link: "https://appwrite.io/discord",
},
{
icon: 'web-icon-github',
label: 'Github',
link: 'https://github.com/appwrite'
icon: "web-icon-github",
label: "Github",
link: "https://github.com/appwrite",
},
{
icon: 'web-icon-x',
label: 'Twitter',
link: 'https://twitter.com/intent/follow?screen_name=appwrite'
icon: "web-icon-x",
label: "Twitter",
link: "https://twitter.com/intent/follow?screen_name=appwrite",
},
{
icon: 'web-icon-linkedin',
label: 'LinkedIn',
link: 'https://www.linkedin.com/company/appwrite'
icon: "web-icon-linkedin",
label: "LinkedIn",
link: "https://www.linkedin.com/company/appwrite",
},
{
icon: 'web-icon-youtube',
label: 'YouTube',
link: 'https://www.youtube.com/c/appwrite?sub_confirmation=1'
}
icon: "web-icon-youtube",
label: "YouTube",
link: "https://www.youtube.com/c/appwrite?sub_confirmation=1",
},
];

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<script lang="ts" generics="T">
import Fuse, { type IFuseOptions, type FuseResult } from 'fuse.js';
import Fuse, { type IFuseOptions, type FuseResult } from "fuse.js";
export let list: ReadonlyArray<T>;
export let options: IFuseOptions<T>;

View File

@@ -1,6 +1,6 @@
import type { FuseResult } from 'fuse.js';
import type { FuseResult } from "fuse.js";
// Reexport your entry components here
export { default as Fuse } from './Fuse.svelte';
export { default as Fuse } from "./Fuse.svelte";
export type ResultType<T> = FuseResult<T>[];

View File

@@ -1,4 +1,4 @@
export { default as Main } from './Main.svelte';
export { default as Docs } from './Docs.svelte';
export { default as DocsArticle } from './DocsArticle.svelte';
export { default as DocsTutorial } from './DocsTutorial.svelte';
export { default as Main } from "./Main.svelte";
export { default as Docs } from "./Docs.svelte";
export { default as DocsArticle } from "./DocsArticle.svelte";
export { default as DocsTutorial } from "./DocsTutorial.svelte";

View File

@@ -18,20 +18,20 @@ interface ChainFn {
<A, B, C>(
fn1: (args: Args<undefined>) => A,
fn2: (args: Args<A>) => B,
fn3: (args: Args<B>) => C
fn3: (args: Args<B>) => C,
): Chain;
<A, B, C, D>(
fn1: (args: Args<undefined>) => A,
fn2: (args: Args<A>) => B,
fn3: (args: Args<B>) => C,
fn4: (args: Args<C>) => D
fn4: (args: Args<C>) => D,
): Chain;
<A, B, C, D, E>(
fn1: (args: Args<undefined>) => A,
fn2: (args: Args<A>) => B,
fn3: (args: Args<B>) => C,
fn4: (args: Args<C>) => D,
fn5: (args: Args<D>) => E
fn5: (args: Args<D>) => E,
): Chain;
<A, B, C, D, E, F>(
fn1: (args: Args<undefined>) => A,
@@ -39,7 +39,7 @@ interface ChainFn {
fn3: (args: Args<B>) => C,
fn4: (args: Args<C>) => D,
fn5: (args: Args<D>) => E,
fn6: (args: Args<E>) => F
fn6: (args: Args<E>) => F,
): Chain;
<A, B, C, D, E, F, G>(
fn1: (args: Args<undefined>) => A,
@@ -48,7 +48,7 @@ interface ChainFn {
fn4: (args: Args<C>) => D,
fn5: (args: Args<D>) => E,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G
fn7: (args: Args<F>) => G,
): Chain;
<A, B, C, D, E, F, G, H>(
fn1: (args: Args<undefined>) => A,
@@ -58,7 +58,7 @@ interface ChainFn {
fn5: (args: Args<D>) => E,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H
fn8: (args: Args<G>) => H,
): Chain;
<A, B, C, D, E, F, G, H, I>(
fn1: (args: Args<undefined>) => A,
@@ -69,7 +69,7 @@ interface ChainFn {
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I
fn9: (args: Args<H>) => I,
): Chain;
<A, B, C, D, E, F, G, H, I, J>(
fn1: (args: Args<undefined>) => A,
@@ -81,7 +81,7 @@ interface ChainFn {
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J
fn10: (args: Args<I>) => J,
): Chain;
<A, B, C, D, E, F, G, H, I, J, K>(
fn1: (args: Args<undefined>) => A,
@@ -94,7 +94,7 @@ interface ChainFn {
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J,
fn11: (args: Args<J>) => K
fn11: (args: Args<J>) => K,
): Chain;
<A, B, C, D, E, F, G, H, I, J, K, L>(
fn1: (args: Args<undefined>) => A,
@@ -108,7 +108,7 @@ interface ChainFn {
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J,
fn11: (args: Args<J>) => K,
fn12: (args: Args<K>) => L
fn12: (args: Args<K>) => L,
): Chain;
// So on...
}

View File

@@ -1,24 +1,24 @@
import { describe, it, expect } from 'vitest';
import { describe, it, expect } from "vitest";
import { clamp } from './clamp';
import type { TestCases } from './test';
import { clamp } from "./clamp";
import type { TestCases } from "./test";
const testCases: TestCases<typeof clamp> = [
{
args: [0, 5, 10],
expected: 5
expected: 5,
},
{
args: [0, -5, 10],
expected: 0
expected: 0,
},
{
args: [0, 15, 10],
expected: 10
}
expected: 10,
},
];
describe('clamp', () => {
describe("clamp", () => {
testCases.forEach(({ args, expected }) => {
it(`should return ${expected} when given ${JSON.stringify(args)}`, () => {
expect(clamp(...args)).toBe(expected);

View File

@@ -1,33 +1,33 @@
import type { LanguageFn } from 'highlight.js';
import hljs from 'highlight.js/lib/core';
import go from 'highlight.js/lib/languages/go';
import dart from 'highlight.js/lib/languages/dart';
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import xml from 'highlight.js/lib/languages/xml';
import shell from 'highlight.js/lib/languages/shell';
import markdown from 'highlight.js/lib/languages/markdown';
import json from 'highlight.js/lib/languages/json';
import swift from 'highlight.js/lib/languages/swift';
import php from 'highlight.js/lib/languages/php';
import python from 'highlight.js/lib/languages/python';
import diff from 'highlight.js/lib/languages/diff';
import ruby from 'highlight.js/lib/languages/ruby';
import csharp from 'highlight.js/lib/languages/csharp';
import kotlin from 'highlight.js/lib/languages/kotlin';
import java from 'highlight.js/lib/languages/java';
import cpp from 'highlight.js/lib/languages/cpp';
import bash from 'highlight.js/lib/languages/bash';
import powershell from 'highlight.js/lib/languages/powershell';
import dos from 'highlight.js/lib/languages/dos';
import yaml from 'highlight.js/lib/languages/yaml';
import plaintext from 'highlight.js/lib/languages/plaintext';
import graphql from 'highlight.js/lib/languages/graphql';
import http from 'highlight.js/lib/languages/http';
import css from 'highlight.js/lib/languages/css';
import groovy from 'highlight.js/lib/languages/groovy';
import ini from 'highlight.js/lib/languages/ini';
import { Platform } from './references';
import type { LanguageFn } from "highlight.js";
import hljs from "highlight.js/lib/core";
import go from "highlight.js/lib/languages/go";
import dart from "highlight.js/lib/languages/dart";
import javascript from "highlight.js/lib/languages/javascript";
import typescript from "highlight.js/lib/languages/typescript";
import xml from "highlight.js/lib/languages/xml";
import shell from "highlight.js/lib/languages/shell";
import markdown from "highlight.js/lib/languages/markdown";
import json from "highlight.js/lib/languages/json";
import swift from "highlight.js/lib/languages/swift";
import php from "highlight.js/lib/languages/php";
import python from "highlight.js/lib/languages/python";
import diff from "highlight.js/lib/languages/diff";
import ruby from "highlight.js/lib/languages/ruby";
import csharp from "highlight.js/lib/languages/csharp";
import kotlin from "highlight.js/lib/languages/kotlin";
import java from "highlight.js/lib/languages/java";
import cpp from "highlight.js/lib/languages/cpp";
import bash from "highlight.js/lib/languages/bash";
import powershell from "highlight.js/lib/languages/powershell";
import dos from "highlight.js/lib/languages/dos";
import yaml from "highlight.js/lib/languages/yaml";
import plaintext from "highlight.js/lib/languages/plaintext";
import graphql from "highlight.js/lib/languages/graphql";
import http from "highlight.js/lib/languages/http";
import css from "highlight.js/lib/languages/css";
import groovy from "highlight.js/lib/languages/groovy";
import ini from "highlight.js/lib/languages/ini";
import { Platform } from "./references";
const languages = {
js: javascript,
@@ -64,33 +64,33 @@ const languages = {
css: css,
groovy: groovy,
ini: ini,
env: ini
env: ini,
} as const satisfies Record<string, LanguageFn>;
const platformAliases: Record<string, keyof typeof languages> = {
[Platform.ClientWeb]: 'js',
[Platform.ClientFlutter]: 'dart',
[Platform.ClientApple]: 'swift',
[Platform.ClientAndroidJava]: 'java',
[Platform.ClientAndroidKotlin]: 'kotlin',
[Platform.ClientReactNative]: 'js',
[Platform.ClientGraphql]: 'graphql',
[Platform.ClientRest]: 'http',
[Platform.ServerDart]: 'dart',
[Platform.ServerDeno]: 'ts',
[Platform.ServerDotNet]: 'cs',
[Platform.ServerNodeJs]: 'js',
[Platform.ServerPhp]: 'php',
[Platform.ServerPython]: 'py',
[Platform.ServerRuby]: 'rb',
[Platform.ServerSwift]: 'swift',
[Platform.ServerJava]: 'java',
[Platform.ServerKotlin]: 'kotlin',
[Platform.ServerGraphql]: 'graphql',
[Platform.ServerRest]: 'http',
[Platform.ServerGo]: 'go',
vue: 'html',
svelte: 'html'
[Platform.ClientWeb]: "js",
[Platform.ClientFlutter]: "dart",
[Platform.ClientApple]: "swift",
[Platform.ClientAndroidJava]: "java",
[Platform.ClientAndroidKotlin]: "kotlin",
[Platform.ClientReactNative]: "js",
[Platform.ClientGraphql]: "graphql",
[Platform.ClientRest]: "http",
[Platform.ServerDart]: "dart",
[Platform.ServerDeno]: "ts",
[Platform.ServerDotNet]: "cs",
[Platform.ServerNodeJs]: "js",
[Platform.ServerPhp]: "php",
[Platform.ServerPython]: "py",
[Platform.ServerRuby]: "rb",
[Platform.ServerSwift]: "swift",
[Platform.ServerJava]: "java",
[Platform.ServerKotlin]: "kotlin",
[Platform.ServerGraphql]: "graphql",
[Platform.ServerRest]: "http",
[Platform.ServerGo]: "go",
vue: "html",
svelte: "html",
};
Object.entries(languages).forEach(([key, value]) => {
@@ -99,7 +99,7 @@ Object.entries(languages).forEach(([key, value]) => {
Object.entries(platformAliases).forEach(([key, value]) => {
hljs.registerAliases(key, {
languageName: value
languageName: value,
});
});
@@ -113,19 +113,19 @@ type Args = {
export const getCodeHtml = (args: Args) => {
const { content, language, withLineNumbers } = args;
const res = hljs.highlight(content, { language: language ?? 'sh' }).value;
const res = hljs.highlight(content, { language: language ?? "sh" }).value;
const lines = res.split(/\n/g);
while (lines.length > 0 && lines[lines.length - 1] === '') {
while (lines.length > 0 && lines[lines.length - 1] === "") {
lines.pop();
}
const final = lines.reduce((carry, line) => {
carry += `<span class="line">${line}</span>\n`;
return carry;
}, '');
}, "");
return `<pre><code class="web-code language-${language} ${
withLineNumbers ? 'line-numbers' : ''
withLineNumbers ? "line-numbers" : ""
}">${final}</code></pre>`;
};

View File

@@ -1,20 +1,20 @@
import { derived, writable } from 'svelte/store';
import { browser } from '$app/environment';
import { Account, Client, Teams } from '@appwrite.io/console';
import { Query, type Models } from '@appwrite.io/console';
import { PUBLIC_APPWRITE_ENDPOINT } from '$env/static/public';
import { derived, writable } from "svelte/store";
import { browser } from "$app/environment";
import { Account, Client, Teams } from "@appwrite.io/console";
import { Query, type Models } from "@appwrite.io/console";
import { PUBLIC_APPWRITE_ENDPOINT } from "$env/static/public";
const client = new Client();
client.setEndpoint(PUBLIC_APPWRITE_ENDPOINT).setProject('console');
client.setEndpoint(PUBLIC_APPWRITE_ENDPOINT).setProject("console");
const account = new Account(client);
const teams = new Teams(client);
enum BillingPlan {
STARTER = 'tier-0',
PRO = 'tier-1',
SCALE = 'tier-2'
STARTER = "tier-0",
PRO = "tier-1",
SCALE = "tier-2",
}
export async function createSource(
@@ -22,7 +22,7 @@ export async function createSource(
referrer: string | null,
utmSource: string | null,
utmCampaign: string | null,
utmMedium: string | null
utmMedium: string | null,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> {
const path = `/console/sources`;
@@ -31,23 +31,25 @@ export async function createSource(
referrer,
utmSource,
utmCampaign,
utmMedium
utmMedium,
};
const uri = new URL(client.config.endpoint + path);
return await client.call(
'POST',
"POST",
uri,
{
'content-type': 'application/json'
"content-type": "application/json",
},
params
params,
);
}
export async function isProUser() {
try {
const orgs = await teams.list([Query.equal('billingPlan', BillingPlan.PRO)]);
const orgs = await teams.list([
Query.equal("billingPlan", BillingPlan.PRO),
]);
return orgs?.teams?.length > 1;
} catch (e) {
return false;
@@ -58,7 +60,7 @@ export const sdk = { client, account };
export type AppwriteUser = Models.User<Models.Preferences>;
function isAppwriteUser(user: unknown): user is AppwriteUser {
return typeof user === 'object' && user !== null && '$id' in user;
return typeof user === "object" && user !== null && "$id" in user;
}
export function getAppwriteUser(): Promise<AppwriteUser | null> {
@@ -73,15 +75,17 @@ function createAppwriteUser() {
function set(v: AppwriteUser | null) {
if (v) {
localStorage.setItem('appwrite:user', JSON.stringify(v));
localStorage.setItem("appwrite:user", JSON.stringify(v));
} else {
localStorage.removeItem('appwrite:user');
localStorage.removeItem("appwrite:user");
}
_set(v);
}
if (browser) {
const localUser = JSON.parse(localStorage.getItem('appwrite:user') ?? 'null');
const localUser = JSON.parse(
localStorage.getItem("appwrite:user") ?? "null",
);
if (isAppwriteUser(localUser)) _set(localUser);
getAppwriteUser().then((res) => {
set(res);
@@ -89,7 +93,7 @@ function createAppwriteUser() {
}
return {
subscribe
subscribe,
};
}

View File

@@ -1,4 +1,4 @@
import { writable } from 'svelte/store';
import { writable } from "svelte/store";
async function securedCopy(value: string) {
try {
@@ -11,13 +11,13 @@ async function securedCopy(value: string) {
}
function unsecuredCopy(value: string) {
const textArea = document.createElement('textarea');
const textArea = document.createElement("textarea");
textArea.value = value;
// Avoid scrolling to bottom
textArea.style.top = '0';
textArea.style.left = '0';
textArea.style.position = 'fixed';
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
document.body.appendChild(textArea);
textArea.focus();
@@ -25,7 +25,7 @@ function unsecuredCopy(value: string) {
let success = true;
try {
document.execCommand('copy');
document.execCommand("copy");
} catch {
success = false;
} finally {
@@ -56,6 +56,6 @@ export function createCopy(value: string) {
return {
copied,
copy: handleCopy
copy: handleCopy,
};
}

View File

@@ -1,6 +1,6 @@
export const formatDate = (date: string | Date | number): string => {
const dt = new Date(date);
const month = dt.toLocaleString('en-US', { month: 'short' });
const month = dt.toLocaleString("en-US", { month: "short" });
const day = dt.getDate();
const year = dt.getFullYear();
return `${month} ${day}, ${year}`;
@@ -11,9 +11,9 @@ export const addDays = (date: Date, days: number) => {
};
export const toReleaseDate = (date: Date) => {
return date.toLocaleDateString('en-US', {
weekday: 'long',
month: 'short',
day: 'numeric'
return date.toLocaleDateString("en-US", {
weekday: "long",
month: "short",
day: "numeric",
});
};

View File

@@ -9,6 +9,6 @@ export function createDebounce(delay = 500) {
immediate: (callback: () => void) => {
clearTimeout(timeout);
callback();
}
},
};
}

View File

@@ -1,9 +1,9 @@
import type { AnimationConfig, FlipParams } from 'svelte/animate';
import { cubicOut } from 'svelte/easing';
import type { AnimationConfig, FlipParams } from "svelte/animate";
import { cubicOut } from "svelte/easing";
// eslint-disable-next-line @typescript-eslint/ban-types
export function is_function(thing: unknown): thing is Function {
return typeof thing === 'function';
return typeof thing === "function";
}
type Params = FlipParams & {
@@ -19,11 +19,11 @@ type Params = FlipParams & {
export function flip(
node: HTMLElement,
{ from, to }: { from: DOMRect; to: DOMRect },
params: Params = {}
params: Params = {},
): AnimationConfig {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
const [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
const transform = style.transform === "none" ? "" : style.transform;
const [ox, oy] = style.transformOrigin.split(" ").map(parseFloat);
const dx = from.left + (from.width * ox) / to.width - (to.left + ox);
const dy = from.top + (from.height * oy) / to.height - (to.top + oy);
@@ -31,11 +31,13 @@ export function flip(
delay = 0,
duration = (d) => Math.sqrt(d) * 120,
easing = cubicOut,
scale = true
scale = true,
} = params;
return {
delay,
duration: is_function(duration) ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
duration: is_function(duration)
? duration(Math.sqrt(dx * dx + dy * dy))
: duration,
easing,
css: (t, u) => {
const x = u * dx;
@@ -52,8 +54,8 @@ export function flip(
const inverse_sx = scale ? 1 / sx : 1;
const inverse_sy = scale ? 1 / sy : 1;
node.style.setProperty('--inverse-sx', inverse_sx.toString());
node.style.setProperty('--inverse-sy', inverse_sy.toString());
}
node.style.setProperty("--inverse-sx", inverse_sx.toString());
node.style.setProperty("--inverse-sy", inverse_sy.toString());
},
};
}

View File

@@ -2,7 +2,7 @@ let lastScrollPos = 0;
export function getScrollDir() {
const scrollPos = window.scrollY;
const scrollDir = scrollPos > lastScrollPos ? 'down' : 'up';
const scrollDir = scrollPos > lastScrollPos ? "down" : "up";
lastScrollPos = scrollPos;
return scrollDir;
}

View File

@@ -1,8 +1,8 @@
export function isNumeric(value: unknown): boolean {
if (typeof value === 'number' && !isNaN(value)) {
if (typeof value === "number" && !isNaN(value)) {
return true;
}
if (typeof value === 'string' && value.trim() !== '') {
if (typeof value === "string" && value.trim() !== "") {
return !isNaN(Number(value));
}
return false;

View File

@@ -1,6 +1,6 @@
export function isVisible(
element: Element,
visRect: { left: number; right: number; top: number; bottom: number }
visRect: { left: number; right: number; top: number; bottom: number },
) {
const rect = element.getBoundingClientRect();
const vertInView = rect.top <= visRect.bottom && rect.bottom >= visRect.top;

View File

@@ -1,35 +1,37 @@
import MarkdownIt from 'markdown-it';
import MarkdownIt from "markdown-it";
const md = new MarkdownIt('commonmark');
const md = new MarkdownIt("commonmark");
export function parse(content: string): string {
const tokens = md.parse(content, null);
return md.renderer.render(
transform_tokens(tokens),
{
highlight: null
highlight: null,
},
null
null,
);
}
function transform_tokens(tokens: ReturnType<typeof md.parse>): ReturnType<typeof md.parse> {
function transform_tokens(
tokens: ReturnType<typeof md.parse>,
): ReturnType<typeof md.parse> {
return tokens.map((token) => {
if (token.children) {
token.children = transform_tokens(token.children);
}
switch (token.type) {
case 'paragraph_open':
token.attrPush(['class', 'web-paragraph']);
case "paragraph_open":
token.attrPush(["class", "web-paragraph"]);
break;
case 'link_open': {
const href = token.attrGet('href');
if (href?.startsWith('http')) {
if (!href.startsWith('https://appwrite.io')) {
token.attrPush(['rel', 'noopener noreferrer']);
token.attrPush(['target', '_blank']);
case "link_open": {
const href = token.attrGet("href");
if (href?.startsWith("http")) {
if (!href.startsWith("https://appwrite.io")) {
token.attrPush(["rel", "noopener noreferrer"]);
token.attrPush(["target", "_blank"]);
}
}
token.attrPush(['class', 'web-link']);
token.attrPush(["class", "web-link"]);
break;
}
}

View File

@@ -1,4 +1,8 @@
export function toScale(value: number, from: [number, number], to: [number, number]) {
export function toScale(
value: number,
from: [number, number],
to: [number, number],
) {
const [fromMin, fromMax] = from;
const [toMin, toMax] = to;

View File

@@ -1,35 +1,38 @@
import type { AuthorData, PostsData } from '$routes/blog/content';
import type { AuthorData, PostsData } from "$routes/blog/content";
export const DEFAULT_HOST = 'https://appwrite.io';
export const DEFAULT_HOST = "https://appwrite.io";
export const DEFAULT_DESCRIPTION =
'Appwrite is an open-source platform for building applications at any scale, using your preferred programming languages and tools.';
export function buildOpenGraphImage(title: string, description: string): string {
"Appwrite is an open-source platform for building applications at any scale, using your preferred programming languages and tools.";
export function buildOpenGraphImage(
title: string,
description: string,
): string {
return `https://og.appwrite.global/image.png?title=${encodeURIComponent(
title
title,
)}&subtitle=${encodeURIComponent(description)}`;
}
export function createSchemaAuthor(author: AuthorData): string {
return (
`< script type="application/ld+json">${JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Person',
"@context": "https://schema.org",
"@type": "Person",
name: author.name,
url: author.href,
image: author.avatar
})}</` + 'script>'
image: author.avatar,
})}</` + "script>"
);
}
export function createSchemaPost(post: PostsData): string {
return (
`< script type="application/ld+json">${JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BlogPosting',
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.title,
image: post.cover,
datePublished: post.date,
dateModified: post.date
})}</` + 'script>'
dateModified: post.date,
})}</` + "script>"
);
}

View File

@@ -1,4 +1,4 @@
import { writable } from 'svelte/store';
import { writable } from "svelte/store";
type Adapter = {
get: (key: string) => unknown;
@@ -7,7 +7,7 @@ type Adapter = {
const localStorageAdapter: Adapter = {
get: (key: string) => {
if (typeof window === 'undefined') return undefined;
if (typeof window === "undefined") return undefined;
const value = localStorage.getItem(key);
@@ -17,10 +17,10 @@ const localStorageAdapter: Adapter = {
return JSON.parse(value);
},
set: (key, value) => {
if (typeof window === 'undefined') return;
if (typeof window === "undefined") return;
localStorage.setItem(key, JSON.stringify(value));
}
},
};
type Options<Value> = {
@@ -31,7 +31,7 @@ type Options<Value> = {
export const persisted = <Value>(
key: string,
{ defaultValue, adapter = localStorageAdapter, validate }: Options<Value>
{ defaultValue, adapter = localStorageAdapter, validate }: Options<Value>,
) => {
const localValue = adapter.get(key);
let initialValue: Value | undefined = defaultValue;
@@ -60,6 +60,6 @@ export const persisted = <Value>(
return {
...store,
update,
set
set,
};
};

View File

@@ -1,4 +1,4 @@
export const isDom = () => typeof window !== 'undefined';
export const isDom = () => typeof window !== "undefined";
export function getPlatform() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const agent = (navigator as any).userAgentData;

View File

@@ -1,147 +1,146 @@
import { writable } from 'svelte/store';
import type { Language } from './code';
import { browser } from '$app/environment';
import { writable } from "svelte/store";
import type { Language } from "./code";
import { browser } from "$app/environment";
const allVersions = [
'1.6.x',
'1.5.x',
'1.4.x',
'1.3.x',
'1.2.x',
'1.1.x',
'1.0.x',
'0.15.x',
'cloud'
"1.6.x",
"1.5.x",
"1.4.x",
"1.3.x",
"1.2.x",
"1.1.x",
"1.0.x",
"0.15.x",
"cloud",
] as const;
export type Version = (typeof allVersions)[number];
export const versions: Readonly<Array<Omit<Version, 'cloud'>>> = allVersions.filter(
(v) => v !== 'cloud'
);
export const versions: Readonly<Array<Omit<Version, "cloud">>> =
allVersions.filter((v) => v !== "cloud");
export enum Service {
Account = 'account',
Avatars = 'avatars',
Databases = 'databases',
Functions = 'functions',
Messaging = 'messaging',
Health = 'health',
Locale = 'locale',
Storage = 'storage',
Teams = 'teams',
Users = 'users',
Account = "account",
Avatars = "avatars",
Databases = "databases",
Functions = "functions",
Messaging = "messaging",
Health = "health",
Locale = "locale",
Storage = "storage",
Teams = "teams",
Users = "users",
}
export enum Platform {
ClientWeb = 'client-web',
ClientFlutter = 'client-flutter',
ClientReactNative = 'client-react-native',
ClientApple = 'client-apple',
ClientAndroidKotlin = 'client-android-kotlin',
ClientAndroidJava = 'client-android-java',
ClientGraphql = 'client-graphql',
ClientRest = 'client-rest',
ServerNodeJs = 'server-nodejs',
ServerPython = 'server-python',
ServerDart = 'server-dart',
ServerPhp = 'server-php',
ServerRuby = 'server-ruby',
ServerDotNet = 'server-dotnet',
ServerDeno = 'server-deno',
ServerGo = 'server-go',
ServerSwift = 'server-swift',
ServerKotlin = 'server-kotlin',
ServerJava = 'server-java',
ServerGraphql = 'server-graphql',
ServerRest = 'server-rest',
ClientWeb = "client-web",
ClientFlutter = "client-flutter",
ClientReactNative = "client-react-native",
ClientApple = "client-apple",
ClientAndroidKotlin = "client-android-kotlin",
ClientAndroidJava = "client-android-java",
ClientGraphql = "client-graphql",
ClientRest = "client-rest",
ServerNodeJs = "server-nodejs",
ServerPython = "server-python",
ServerDart = "server-dart",
ServerPhp = "server-php",
ServerRuby = "server-ruby",
ServerDotNet = "server-dotnet",
ServerDeno = "server-deno",
ServerGo = "server-go",
ServerSwift = "server-swift",
ServerKotlin = "server-kotlin",
ServerJava = "server-java",
ServerGraphql = "server-graphql",
ServerRest = "server-rest",
}
export const platformMap: Record<Language | string, string> = {
[Platform.ClientApple]: 'Apple',
[Platform.ClientFlutter]: 'Flutter',
[Platform.ClientWeb]: 'Web',
[Platform.ClientAndroidKotlin]: 'Android (Kotlin)',
[Platform.ClientAndroidJava]: 'Android (Java)',
[Platform.ClientReactNative]: 'React Native',
[Platform.ClientGraphql]: 'GraphQL',
[Platform.ClientRest]: 'REST',
[Platform.ServerDart]: 'Dart',
[Platform.ServerDeno]: 'Deno',
[Platform.ServerDotNet]: '.NET',
[Platform.ServerNodeJs]: 'Node.js',
[Platform.ServerPhp]: 'PHP',
[Platform.ServerPython]: 'Python',
[Platform.ServerRuby]: 'Ruby',
[Platform.ServerSwift]: 'Swift',
[Platform.ServerKotlin]: 'Kotlin',
[Platform.ServerJava]: 'Java',
[Platform.ServerGraphql]: 'GraphQL',
[Platform.ServerRest]: 'REST',
[Platform.ServerGo]: 'Go',
sh: 'Shell',
js: 'JavaScript',
ts: 'TypeScript',
jsx: 'React',
tsx: 'React',
typescript: 'TypeScript',
dart: 'Dart',
java: 'Java',
kotlin: 'Kotlin',
cs: 'C#',
py: 'Python',
rb: 'Ruby',
php: 'PHP',
swift: 'Swift',
xml: 'XML',
html: 'HTML',
md: 'Markdown',
json: 'JSON',
diff: 'Diff',
http: 'HTTP',
css: 'CSS',
graphql: 'GraphQL',
deno: 'Deno',
python: 'Python',
ruby: 'Ruby',
csharp: 'C#',
cpp: 'C++',
bash: 'Bash',
powershell: 'PowerShell',
cmd: 'CMD',
yaml: 'YAML',
text: 'Text',
vue: 'Vue',
svelte: 'Svelte',
groovy: 'Groovy',
go: 'Go',
[Platform.ClientApple]: "Apple",
[Platform.ClientFlutter]: "Flutter",
[Platform.ClientWeb]: "Web",
[Platform.ClientAndroidKotlin]: "Android (Kotlin)",
[Platform.ClientAndroidJava]: "Android (Java)",
[Platform.ClientReactNative]: "React Native",
[Platform.ClientGraphql]: "GraphQL",
[Platform.ClientRest]: "REST",
[Platform.ServerDart]: "Dart",
[Platform.ServerDeno]: "Deno",
[Platform.ServerDotNet]: ".NET",
[Platform.ServerNodeJs]: "Node.js",
[Platform.ServerPhp]: "PHP",
[Platform.ServerPython]: "Python",
[Platform.ServerRuby]: "Ruby",
[Platform.ServerSwift]: "Swift",
[Platform.ServerKotlin]: "Kotlin",
[Platform.ServerJava]: "Java",
[Platform.ServerGraphql]: "GraphQL",
[Platform.ServerRest]: "REST",
[Platform.ServerGo]: "Go",
sh: "Shell",
js: "JavaScript",
ts: "TypeScript",
jsx: "React",
tsx: "React",
typescript: "TypeScript",
dart: "Dart",
java: "Java",
kotlin: "Kotlin",
cs: "C#",
py: "Python",
rb: "Ruby",
php: "PHP",
swift: "Swift",
xml: "XML",
html: "HTML",
md: "Markdown",
json: "JSON",
diff: "Diff",
http: "HTTP",
css: "CSS",
graphql: "GraphQL",
deno: "Deno",
python: "Python",
ruby: "Ruby",
csharp: "C#",
cpp: "C++",
bash: "Bash",
powershell: "PowerShell",
cmd: "CMD",
yaml: "YAML",
text: "Text",
vue: "Vue",
svelte: "Svelte",
groovy: "Groovy",
go: "Go",
};
export const serviceMap: Record<Service, string> = {
[Service.Account]: 'Account',
[Service.Avatars]: 'Avatars',
[Service.Databases]: 'Databases',
[Service.Functions]: 'Functions',
[Service.Messaging]: 'Messaging',
[Service.Health]: 'Health',
[Service.Locale]: 'Locale',
[Service.Storage]: 'Storage',
[Service.Teams]: 'Teams',
[Service.Users]: 'Users',
[Service.Account]: "Account",
[Service.Avatars]: "Avatars",
[Service.Databases]: "Databases",
[Service.Functions]: "Functions",
[Service.Messaging]: "Messaging",
[Service.Health]: "Health",
[Service.Locale]: "Locale",
[Service.Storage]: "Storage",
[Service.Teams]: "Teams",
[Service.Users]: "Users",
};
export const preferredVersion = writable<Version | null>(
globalThis?.localStorage?.getItem('preferredVersion') as Version
globalThis?.localStorage?.getItem("preferredVersion") as Version,
);
export const preferredPlatform = writable<Platform | null>(
globalThis?.localStorage?.getItem('preferredPlatform') as Platform
globalThis?.localStorage?.getItem("preferredPlatform") as Platform,
);
if (browser) {
preferredVersion.subscribe((value) => {
if (value) globalThis?.sessionStorage?.setItem('preferredVersion', value);
if (value) globalThis?.sessionStorage?.setItem("preferredVersion", value);
});
preferredPlatform.subscribe((value) => {
if (value) globalThis?.sessionStorage?.setItem('preferredPlatform', value);
if (value) globalThis?.sessionStorage?.setItem("preferredPlatform", value);
});
}

Some files were not shown because too many files have changed in this diff Show More