Merge branch 'main' into skip-e2e

This commit is contained in:
Darshan
2025-04-03 13:46:31 +05:30
committed by GitHub
58 changed files with 487 additions and 2356 deletions

1
global.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module 'reodotdev';

View File

@@ -42,9 +42,8 @@
"@melt-ui/pp": "^0.3.2",
"@melt-ui/svelte": "^0.86.5",
"@playwright/test": "^1.50.0",
"@sentry/sveltekit": "^9.10.1",
"@sveltejs/adapter-node": "^4.0.1",
"@sveltejs/enhanced-img": "^0.3.9",
"@sveltejs/adapter-node": "^5.2.12",
"@sveltejs/enhanced-img": "^0.4.4",
"@sveltejs/kit": "^2.20.2",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/postcss": "^4.0.17",
@@ -81,6 +80,7 @@
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"remeda": "^2.20.0",
"reodotdev": "^1.0.0",
"sass": "^1.83.4",
"svelte": "^5.25.6",
"svelte-check": "^4.0.0",
@@ -96,12 +96,11 @@
"vite-plugin-dynamic-import": "^1.6.0",
"vite-plugin-image-optimizer": "^1.1.8",
"vite-plugin-manifest-sri": "^0.2.0",
"vitest": "^1.6.0"
"vitest": "^3.1.1"
},
"pnpm": {
"onlyBuiltDependencies": [
"@parcel/watcher",
"@sentry/cli",
"core-js",
"esbuild",
"sharp",

2121
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +0,0 @@
import { dev } from '$app/environment';
import { SENTRY_DSN } from '$lib/constants';
import * as Sentry from '@sentry/sveltekit';
import { handleErrorWithSentry } from '@sentry/sveltekit';
Sentry.init({
enabled: !dev,
dsn: SENTRY_DSN,
allowUrls: [/appwrite\.io/],
tracesSampleRate: 1.0,
// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0,
// If the entire session is not sampled, use the below sample rate to sample
// sessions when an error occurs.
replaysOnErrorSampleRate: 0
});
// If you have a custom error handler, pass it to `handleErrorWithSentry`
export const handleError = handleErrorWithSentry();

View File

@@ -1,135 +0,0 @@
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/]
});
const redirectMap = new Map(redirects.map(({ link, redirect }) => [link, redirect]));
const redirecter: Handle = async ({ event, resolve }) => {
const currentPath = event.url.pathname;
if (redirectMap.has(currentPath)) {
return new Response(null, {
status: 308,
headers: {
location: redirectMap.get(currentPath) ?? ''
}
});
}
return await resolve(event);
};
const securityheaders: Handle = async ({ event, resolve }) => {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
(event.locals as { nonce: string }).nonce = nonce;
const response = await resolve(event, {
transformPageChunk: ({ html }) => {
return html.replace(/%sveltekit.nonce%/g, nonce);
}
});
// `true` if deployed via Coolify.
const isPreview = !!process.env.COOLIFY_FQDN;
// COOLIFY_FQDN already includes `http`.
const previewDomain = isPreview ? `${process.env.COOLIFY_FQDN}` : null;
const join = (arr: string[]) => arr.join(' ');
const cspDirectives: Record<string, string> = {
'default-src': "'self'",
'script-src': join([
"'self'",
'blob:',
"'unsafe-inline'",
"'unsafe-eval'",
'https://*.posthog.com',
'https://*.plausible.io',
'https://*.reo.dev',
'https://plausible.io',
'https://js.zi-scripts.com',
'https://ws.zoominfo.com'
]),
'style-src': "'self' 'unsafe-inline'",
'img-src': "'self' data: https:",
'font-src': "'self'",
'object-src': "'none'",
'base-uri': "'self'",
'form-action': "'self'",
'frame-ancestors': join(["'self'", 'https://www.youtube.com', 'https://*.vimeo.com']),
'block-all-mixed-content': '',
'upgrade-insecure-requests': '',
'connect-src': join([
"'self'",
'https://*.appwrite.io',
'https://*.appwrite.org',
'https://*.posthog.com',
'https://*.sentry.io',
'https://*.plausible.io',
'https://plausible.io',
'https://*.reo.dev',
'https://js.zi-scripts.com',
'https://aorta.clickagy.com',
'https://hemsync.clickagy.com',
'https://ws.zoominfo.com '
]),
'frame-src': join([
"'self'",
'https://www.youtube.com',
'https://status.appwrite.online',
'https://www.youtube-nocookie.com',
'https://player.vimeo.com',
'https://hemsync.clickagy.com'
])
};
if (isPreview) {
delete cspDirectives['block-all-mixed-content'];
delete cspDirectives['upgrade-insecure-requests'];
['default-src', 'script-src', 'style-src', 'img-src', 'font-src', 'connect-src'].forEach(
(key) => {
cspDirectives[key] += ` ${previewDomain}`;
}
);
}
const cspDirectivesString = Object.entries(cspDirectives)
.map(([key, value]) => `${key} ${value}`.trim())
.join('; ');
// Set security headers
response.headers.set('Content-Security-Policy', cspDirectivesString);
// HTTP Strict Transport Security
// max-age is set to 1 year in seconds
response.headers.set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
// X-Content-Type-Options
response.headers.set('X-Content-Type-Options', 'nosniff');
// X-Frame-Options
response.headers.set('X-Frame-Options', 'DENY');
return response;
};
const bannerRewriter: Handle = async ({ event, resolve }) => {
const response = await resolve(event, {
transformPageChunk: ({ html }) => html.replace('%aw_banner_key%', BANNER_KEY)
});
return response;
};
export const handle = sequence(Sentry.sentryHandle(), redirecter, bannerRewriter, securityheaders);
export const handleError = Sentry.handleErrorWithSentry();

View File

@@ -1,8 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let carousel: HTMLElement;
export let size: 'default' | 'medium' | 'big' = 'default';
export let gap = 32;
interface Props {
size?: 'default' | 'medium' | 'big';
gap?: number;
header: Snippet;
children: Snippet;
}
let { size = 'default', gap = 32, header, children }: Props = $props();
let scroll = 0;
function calculateScrollAmount(prev = false) {
@@ -32,8 +40,8 @@
});
}
let isEnd = false;
let isStart = true;
let isEnd = $state(false);
let isStart = $state(true);
function handleScroll() {
isStart = carousel.scrollLeft <= 0;
@@ -43,13 +51,13 @@
<div>
<div class="mt-2 flex flex-wrap items-center">
<slot name="header" />
{@render header()}
<div class="nav ml-auto flex items-end gap-3">
<button
class="web-icon-button"
aria-label="Move carousel backward"
disabled={isStart}
on:click={prev}
onclick={prev}
>
<span class="web-icon-arrow-left" aria-hidden="true"></span>
</button>
@@ -57,7 +65,7 @@
class="web-icon-button"
aria-label="Move carousel forward"
disabled={isEnd}
on:click={next}
onclick={next}
>
<span class="web-icon-arrow-right" aria-hidden="true"></span>
</button>
@@ -71,9 +79,9 @@
class:is-big={size === 'big'}
style:gap="{gap}px"
bind:this={carousel}
on:scroll={handleScroll}
onscroll={handleScroll}
>
<slot />
{@render children()}
</ul>
</div>
</div>

View File

@@ -1,3 +1,7 @@
<!-- @migration-task Error while migrating Svelte code: Cannot use `export let` in runes mode — use `$props()` instead
https://svelte.dev/e/legacy_export_invalid -->
<!-- @migration-task Error while migrating Svelte code: Cannot use `export let` in runes mode — use `$props()` instead
https://svelte.dev/e/legacy_export_invalid -->
<script lang="ts">
import { Select, Tooltip } from '$lib/components';
import { getCodeHtml, type Language } from '$lib/utils/code';
@@ -26,8 +30,11 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText: CopyStatusValue = CopyStatus.Copy;
let copyText = CopyStatus.Copy;
async function handleCopy() {
await copy(content);
@@ -78,9 +85,11 @@
aria-label="copy code from code-snippet"
><span class="web-icon-copy" aria-hidden="true"></span></button
>
<svelte:fragment slot="tooltip">
{copyText}
</svelte:fragment>
{#snippet tooltip()}
<span>
{copyText}
</span>
{/snippet}
</Tooltip>
</li>
</ul>

View File

@@ -85,7 +85,9 @@
/>
</a>
</li>
<svelte:fragment slot="tooltip">{platform.name}</svelte:fragment>
{#snippet tooltip()}
{platform.name}
{/snippet}
</Tooltip>
{/each}
</ul>

View File

@@ -72,15 +72,15 @@
);
</script>
{@render asChild?.({ trigger: $trigger })}
{#if !asChild && children}
{#if asChild}
{@render asChild({ trigger: $trigger })}
{:else if children}
<span use:melt={$trigger}>
{@render children()}
</span>
{/if}
{#if $open && !disabled}
{#if tooltip && $open && !disabled}
<div use:melt={$content} class="web-tooltip text-sub-body" transition:fly={flyParams}>
<div use:melt={$arrow}></div>
{@render tooltip()}

View File

@@ -1,36 +1,41 @@
<!-- @migration-task Error while migrating Svelte code: migrating this component would require adding a `$props` rune but there's already a variable named props.
Rename the variable and try again or migrate by hand. -->
<script lang="ts">
import { classNames } from '$lib/utils/classnames';
import type { Snippet } from 'svelte';
import type { HTMLInputAttributes } from 'svelte/elements';
type $$Props = HTMLInputAttributes & {
interface Props extends HTMLInputAttributes {
label?: string;
};
icon?: Snippet;
}
export let label: $$Props['label'] = '';
export let type: $$Props['type'] = 'text';
export let value: $$Props['value'] = '';
const { class: classes, name, ...props } = $$restProps;
let {
label = '',
type = 'text',
value = $bindable(''),
icon,
class: classes,
name,
...rest
}: Props = $props();
</script>
{#if $$slots.icon}
{#if icon}
<label
class={classNames(
'focus:border-greyscale-100 bg-greyscale-800 border-greyscale-700 flex items-center gap-1 rounded-lg border px-3 py-2 text-sm font-light transition-colors focus-within:border-white active:shadow-sm active:shadow-black/30',
classes
)}
>
<slot name="icon" />
{@render icon?.()}
{#key type}
<input
{name}
{...{ type }}
bind:value
on:input
on:change
on:focus
on:blur
class="w-full border-0 ring-0 outline-none"
{...props}
{...rest}
/>
{/key}
</label>
@@ -45,15 +50,11 @@
{name}
{...{ type }}
bind:value
on:input
on:change
on:focus
on:blur
class={classNames(
'focus:border-greyscale-100 bg-greyscale-800 border-greyscale-700 mt-2 flex w-full items-center gap-1 rounded-lg border px-3 py-2 text-sm font-light transition-colors focus-within:border-white active:shadow-sm active:shadow-black/30',
classes
)}
{...props}
{...rest}
/>
{/key}
{/if}

View File

@@ -1,8 +1,6 @@
export const GITHUB_STARS = '47K';
export const GITHUB_REPO_LINK = 'https://github.com/appwrite/appwrite';
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';
export const BLOG_POSTS_PER_PAGE = 12;

View File

@@ -72,7 +72,9 @@
{#if expandable && !$layoutState.showSidenav}
<Tooltip placement="right">
<SidebarNavButton groupItem={navGroup} />
<svelte:fragment slot="tooltip">{navGroup.label}</svelte:fragment>
{#snippet tooltip()}
<span>{navGroup.label}</span>
{/snippet}
</Tooltip>
{:else}
<SidebarNavButton groupItem={navGroup} />
@@ -89,9 +91,9 @@
{#if expandable && !$layoutState.showSidenav}
<Tooltip placement="right">
<SidebarNavButton {groupItem} />
<svelte:fragment slot="tooltip"
>{groupItem.label}</svelte:fragment
>
{#snippet tooltip()}
<span>{groupItem.label}</span>
{/snippet}
</Tooltip>
{:else}
<SidebarNavButton {groupItem} />

View File

@@ -35,8 +35,11 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText = $state<CopyStatusValue>(CopyStatus.Copy);
let copyText = $state(CopyStatus.Copy);
async function handleCopy() {
await copy(toCopy ?? content);

View File

@@ -52,8 +52,10 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText = $state(CopyStatus.Copy);
let copyText = $state<CopyStatusValue>(CopyStatus.Copy);
async function handleCopy() {
await copy($content);

View File

@@ -1,5 +1,6 @@
<script lang="ts" context="module">
import { derived, writable } from 'svelte/store';
import { loadReoScript } from 'reodotdev';
export type Theme = 'dark' | 'light' | 'system';
export const currentTheme = (function () {
@@ -128,6 +129,15 @@
}
});
}
if (!dev && browser) {
const clientID = '144fa7eaa4904e8';
const reoPromise = loadReoScript({ clientID });
reoPromise.then((Reo: any) => {
Reo.init({ clientID });
});
}
</script>
<svelte:window on:scroll={handleScroll} />
@@ -135,6 +145,7 @@
{#if !dev}
<!--suppress JSUnresolvedLibraryURL -->
<script defer data-domain="appwrite.io" src="https://plausible.io/js/script.js"></script>
<!-- ZoomInfo snippet -->
<script>
window[
@@ -178,22 +189,6 @@
document.body.appendChild(zi);
});
</script>
<!-- Reo.dev -->
<script type="text/javascript">
!(function () {
var e, t, n;
(e = '144fa7eaa4904e8'),
(t = function () {
Reo.init({ clientID: '144fa7eaa4904e8' });
}),
((n = document.createElement('script')).src =
'https://static.reo.dev/' + e + '/reo.js'),
(n.defer = !0),
(n.onload = t),
document.head.appendChild(n);
})();
</script>
{/if}
<!-- canonical url -->

View File

@@ -2,10 +2,10 @@
import { setContext } from 'svelte';
import type { AuthorData, CategoryData, PostsData } from './content.js';
export let data;
let { data, children } = $props();
setContext<PostsData[]>('posts', data.posts);
setContext<AuthorData[]>('authors', data.authors);
setContext<CategoryData[]>('categories', data.categories);
</script>
<slot />
{@render children()}

View File

@@ -8,20 +8,20 @@
import { beforeNavigate, goto } from '$app/navigation';
import { createDebounce } from '$lib/utils/debounce';
export let data;
let { data } = $props();
const featured = data.featured;
const categories = data.filteredCategories.sort((a, b) => a.name.localeCompare(b.name));
$: isFirstPage = data.currentPage === 1;
let isFirstPage = $derived(data.currentPage === 1);
$: isLastPage = data.currentPage === data.totalPages;
let isLastPage = $derived(data.currentPage === data.totalPages);
$: currentPageRange = data.navigation || [];
let currentPageRange = $derived(data.navigation || []);
let query = '';
let isEnd = false;
let isStart = true;
let query = $state('');
let isEnd = $state(false);
let isStart = $state(true);
let categoriesElement: HTMLElement;
let articlesHeader: HTMLElement;
@@ -41,7 +41,7 @@
});
});
let selectedCategory = $page.url.searchParams.get('category') ?? 'Latest';
let selectedCategory = $state($page.url.searchParams.get('category') ?? 'Latest');
const handleSearch = async () => {
const searchQuery = query.toLowerCase();
@@ -70,7 +70,7 @@
});
};
$: navigationLink = (pageNumber: number): string => {
let navigationLink = $derived((pageNumber: number): string => {
const currentUrl = $page.url;
const url = new URL(`/blog/${pageNumber}`, currentUrl);
@@ -79,7 +79,7 @@
}
return url.toString();
};
});
const { debounce, reset } = createDebounce();
@@ -271,14 +271,14 @@
>
<ul
class="categories flex gap-2 overflow-x-auto"
on:scroll={handleScroll}
onscroll={handleScroll}
bind:this={categoriesElement}
>
<li class="flex items-center">
<button
class="web-interactive-tag web-caption-400 cursor-pointer"
class:is-selected={selectedCategory === 'Latest'}
on:click={() => {
onclick={() => {
selectedCategory = 'Latest';
handleSearch();
}}
@@ -292,7 +292,7 @@
<button
class="web-interactive-tag web-caption-400 cursor-pointer"
class:is-selected={selectedCategory === category.name}
on:click={() => {
onclick={() => {
selectedCategory = category.name;
handleSearch();
}}
@@ -351,7 +351,7 @@
<button
class="web-button is-secondary"
on:click={() => {
onclick={() => {
query = '';
selectedCategory = 'Latest';
handleSearch();

View File

@@ -30,7 +30,7 @@ export type PostsData = {
slug: string;
featured?: boolean;
unlisted?: boolean;
callToAction: {
callToAction?: {
heading?: string;
label?: string;
url?: string;

View File

@@ -9,7 +9,7 @@
import { CHANGELOG_KEY } from '../utils';
import { TITLE_SUFFIX } from '$routes/titles';
export let data;
let { data } = $props();
const seo = {
title: 'Changelog' + TITLE_SUFFIX,
@@ -55,15 +55,14 @@
<li>
<div class="web-dot"></div>
<ChangelogEntry {entry}>
<svelte:component this={entry.component} />
<entry.component />
</ChangelogEntry>
</li>
{/each}
</ul>
{#if data.nextPage}
<button class="web-button is-secondary" on:click={loadMore}
>Load more</button
<button class="web-button is-secondary" onclick={loadMore}>Load more</button
>
{/if}
</div>

View File

@@ -9,7 +9,7 @@
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
import { CHANGELOG_TITLE_SUFFIX } from '$routes/titles';
export let data;
let { data } = $props();
const seo = {
title: data.title,
@@ -25,8 +25,11 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText = $state<CopyStatusValue>(CopyStatus.Copy);
let copyText = CopyStatus.Copy;
async function handleCopy() {
const blogPostUrl = encodeURI(`https://appwrite.io${$page.url.pathname}`);
@@ -126,11 +129,11 @@
</button>
{/if}
<svelte:fragment slot="tooltip">
{#snippet tooltip()}
{sharingOption.type === 'copy'
? copyText
: `Share on ${sharingOption.label}`}
</svelte:fragment>
{/snippet}
</Tooltip>
</li>
{/each}

View File

@@ -1,4 +1,4 @@
<script lang="ts" context="module">
<script lang="ts" module>
export const events: EventCardProps[] = [
{
href: 'https://discord.com/events/564160730845151244/1279026334496067669/1286356126924800000',
@@ -43,6 +43,8 @@
</script>
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { Carousel } from '$lib/components';
import FloatingHeads from '$lib/components/FloatingHeads.svelte';
import FooterNav from '$lib/components/FooterNav.svelte';
@@ -59,7 +61,7 @@
import type { ProjectCardProps } from './ProjectCard.svelte';
import ProjectCard from './ProjectCard.svelte';
export let data;
let { data } = $props();
const projects: ProjectCardProps[] = [
{
@@ -103,11 +105,11 @@
{ metric: '800+', description: 'Contributors' }
];
let name = '';
let email = '';
let submitted = false;
let error: string | undefined;
let submitting = false;
let name = $state('');
let email = $state('');
let submitted = $state(false);
let error: string | undefined = $state();
let submitting = $state(false);
async function submit() {
submitting = true;
@@ -423,9 +425,9 @@
<div class="web-big-padding-section-level-2">
<section class="web-u-sep-block-start web-u-padding-block-start-64 container">
<Carousel size="big">
<svelte:fragment slot="header">
{#snippet header()}
<h4 class="text-label text-primary">Upcoming Events</h4>
</svelte:fragment>
{/snippet}
{#each events as event}
<li>
<EventCard
@@ -630,7 +632,10 @@
{:else}
<form
method="post"
on:submit|preventDefault={submit}
onsubmit={(e) => {
e.preventDefault();
submit();
}}
class="flex flex-col gap-4"
>
<div class="flex flex-col gap-1">

View File

@@ -106,6 +106,7 @@ If running in production, it might be easier to use a 3rd party SMTP server as i
| `_APP_STORAGE_S3_SECRET` | **version >= 0.13.0** AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console. |
| `_APP_STORAGE_S3_REGION` | **version >= 0.13.0** AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console. |
| `_APP_STORAGE_S3_BUCKET` | **version >= 0.13.0** AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console. |
| `_APP_STORAGE_S3_ENDPOINT` | **version >= 1.7.0** Override the S3 endpoint to use an S3-compatible provider. This should just be the host (without 'https://'). |
| `_APP_STORAGE_DO_SPACES_ACCESS_KEY` | **version >= 0.13.0** DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console. |
| `_APP_STORAGE_DO_SPACES_SECRET` | **version >= 0.13.0** DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console. |
| `_APP_STORAGE_DO_SPACES_REGION` | **version >= 0.13.0** DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console. |

View File

@@ -27,13 +27,13 @@
import Response from './(components)/Response.svelte';
import RateLimits from './(components)/RateLimits.svelte';
export let data;
let { data } = $props();
setContext<LayoutContext>('headings', writable({}));
const headings = getContext<LayoutContext>('headings');
let selected: string | undefined = undefined;
let selected: string | undefined = $state(undefined);
headings.subscribe((n) => {
const noVisible = Object.values(n).every((n) => !n.visible);
if (selected && noVisible) {
@@ -119,21 +119,22 @@
});
// cleaned service description without Markdown links.
$: serviceDescription = (data.service?.description ?? '').replace(
/\[([^\]]+)]\([^)]+\)/g,
'$1'
let serviceDescription = $derived(
(data.service?.description ?? '').replace(/\[([^\]]+)]\([^)]+\)/g, '$1')
);
// the service description up to the first full stop, providing sufficient information.
$: shortenedDescription = serviceDescription.substring(0, serviceDescription.indexOf('.') + 1);
let shortenedDescription = $derived(
serviceDescription.substring(0, serviceDescription.indexOf('.') + 1)
);
$: platformBindingForSelect = $page.params.platform as Platform;
$: platform = ($preferredPlatform ?? $page.params.platform) as Platform;
$: platformType = platform.startsWith('client-') ? 'CLIENT' : 'SERVER';
$: serviceName = serviceMap[data.service?.name];
$: title = serviceName + API_REFERENCE_TITLE_SUFFIX;
$: description = shortenedDescription;
$: ogImage = DEFAULT_HOST + '/images/open-graph/docs.png';
let platformBindingForSelect = $derived($page.params.platform as Platform);
let platform = $derived(($preferredPlatform ?? $page.params.platform) as Platform);
let platformType = $derived(platform.startsWith('client-') ? 'CLIENT' : 'SERVER');
let serviceName = $derived(serviceMap[data.service?.name]);
let title = $derived(serviceName + API_REFERENCE_TITLE_SUFFIX);
let description = $derived(shortenedDescription);
let ogImage = $derived(DEFAULT_HOST + '/images/open-graph/docs.png');
</script>
<svelte:head>
@@ -302,7 +303,7 @@
use:clickOutside={() => ($layoutState.showReferences = false)}
>
{#if data.methods.length > 0}
<button class="web-icon-button" id="refOpen" on:click={toggleReferences}>
<button class="web-icon-button" id="refOpen" onclick={toggleReferences}>
<span class="icon-menu-alt-4" aria-hidden="true"></span>
</button>
<div class="web-references-menu-content">
@@ -310,7 +311,7 @@
class="web-references-menu-header mt-6 flex items-center justify-between gap-4"
>
<h5 class="web-references-menu-title text-micro uppercase">On This Page</h5>
<button class="web-icon-button" id="refClose" on:click={toggleReferences}>
<button class="web-icon-button" id="refClose" onclick={toggleReferences}>
<span class="icon-x" aria-hidden="true"></span>
</button>
</div>

View File

@@ -6,7 +6,7 @@
import Tabs from '$markdoc/tags/Tabs.svelte';
import TabsItem from '$markdoc/tags/TabsItem.svelte';
export let data;
let { data } = $props();
</script>
<svelte:head>

View File

@@ -8,7 +8,7 @@
'Follow a simple tutorial to get started with Appwrite in your preferred framework quickly and easily.';
const ogImage = DEFAULT_HOST + '/images/open-graph/docs.png';
export let data;
let { data } = $props();
type MappedTutorial = (typeof data.tutorials)[number];

View File

@@ -55,13 +55,14 @@ export async function load() {
const tutorials = Object.entries(
allTutorials.reduce((acc: { [key: string]: any[] }, item) => {
const cat = item.category as string;
// If the category does not exist in the accumulator, initialize it
if (!acc[item.category]) {
acc[item.category] = [];
if (!acc[cat]) {
acc[cat] = [];
}
// Push the current item into the appropriate category
acc[item.category].push(item);
acc[cat].push(item);
return acc;
}, {})

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -2,9 +2,9 @@
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
let { data, children } = $props();
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />
{@render children()}

View File

@@ -7,7 +7,7 @@
import TicketPreview from '$routes/init-0/(components)/TicketPreview.svelte';
import Ticket from '../../(components)/Ticket.svelte';
export let data;
let { data } = $props();
let firstName = data.ticket?.name?.split(/\s/)[0] ?? '';
const ogImage = `${$page.url.origin}/init-0/tickets/${data.ticket.$id}/og`;
@@ -56,7 +56,7 @@
<a class="web-button" href="/init-0/tickets">
<span class="text">Get my Init ticket</span>
</a>
<button class="web-button is-secondary" on:click={copy}>
<button class="web-button is-secondary" onclick={copy}>
<span class="web-icon-{$copied ? 'check' : 'copy'} text-primary"></span>
<span class="text">Copy ticket URL</span>

View File

@@ -11,23 +11,25 @@
import Form from './form.svelte';
import type { TicketVariant } from '../constants';
export let data;
let { data } = $props();
let name = data.ticket?.name ?? '';
let name = $state(data.ticket?.name ?? '');
const id = data.ticket?.id ?? 0;
let tribe: string | undefined = data.ticket?.tribe ?? undefined;
let showGitHub = data.ticket?.show_contributions ?? true;
let drawerOpen = false;
let customizing = false;
let variant: TicketVariant = data.ticket.variant ?? 'default';
let tribe: string | undefined = $state(data.ticket?.tribe ?? undefined);
let showGitHub = $state(data.ticket?.show_contributions ?? true);
let drawerOpen = $state(false);
let customizing = $state(false);
let variant: TicketVariant = $state(data.ticket.variant ?? 'default');
$: modified = !dequal(
{
name: data.ticket?.name,
tribe: data.ticket?.tribe,
showGitHub: data.ticket?.show_contributions
},
{ name, tribe, showGitHub }
let modified = $derived(
!dequal(
{
name: data.ticket?.name,
tribe: data.ticket?.tribe,
showGitHub: data.ticket?.show_contributions
},
{ name, tribe, showGitHub }
)
);
async function saveTicket() {
@@ -57,12 +59,14 @@
const ticketUrl = `${$page.url.origin}/init-0/tickets/${data.ticket.$id}`;
const { copied, copy } = createCopy(ticketUrl);
$: twitterText = encodeURIComponent(
[
`Join Init and celebrate everything new with @appwrite`,
``,
`Claim your ticket. ${ticketUrl}`
].join('\n')
let twitterText = $derived(
encodeURIComponent(
[
`Join Init and celebrate everything new with @appwrite`,
``,
`Claim your ticket. ${ticketUrl}`
].join('\n')
)
);
</script>
@@ -78,7 +82,7 @@
<div class="hero">
{#if customizing}
<div style:margin-block-start="0.625rem">
<button class="web-link is-secondary u-cross-center" on:click={goBack}>
<button class="web-link is-secondary u-cross-center" onclick={goBack}>
<span class="web-icon-chevron-left" aria-hidden="true"></span>
<span>Back</span>
@@ -110,14 +114,14 @@
<div class="info">
<button
on:click={() => (customizing = true)}
onclick={() => (customizing = true)}
class="web-button is-full-width u-margin-block-start-32"
>
<span class="text">Customize ticket</span>
</button>
<div class="u-flex u-cross-center u-gap-16 u-margin-block-start-16">
<button class="web-button is-full-width is-secondary" on:click={copy}>
<button class="web-button is-full-width is-secondary" onclick={copy}>
<div class="web-icon-{$copied ? 'check' : 'copy'} text-primary"></div>
<span class="text">Copy ticket URL</span>
</button>
@@ -155,7 +159,7 @@
{#if customizing}
<div class="drawer" data-state={drawerOpen ? 'open' : 'closed'}>
<button on:click={() => (drawerOpen = !drawerOpen)}>
<button onclick={() => (drawerOpen = !drawerOpen)}>
<div class="inner">
<span class="text-label text-primary">Ticket Editor</span>
<span class="web-icon-chevron-down"></span>

View File

@@ -29,7 +29,9 @@
>
<img {src} {alt} />
</button>
<svelte:fragment slot="tooltip">{alt}</svelte:fragment>
{#snippet tooltip()}
{alt}
{/snippet}
</Tooltip>
<style lang="scss">

View File

@@ -57,7 +57,9 @@
<Tooltip placement="bottom">
<Switch bind:checked={showGitHub} />
<svelte:fragment slot="tooltip">Show GitHub contributions</svelte:fragment>
{#snippet tooltip()}
Show GitHub contributions
{/snippet}
</Tooltip>
</div>
{:else}

View File

@@ -6,7 +6,7 @@
import { createCopy } from '$lib/utils/copy';
import { TicketPreview, Ticket } from '$routes/init/(components)/ticket';
export let data;
let { data } = $props();
let firstName = data.ticket?.name?.split(/\s/)[0] ?? '';
const ogImage = `${$page.url.origin}/init/tickets/${data.ticket.$id}/og`;
@@ -52,7 +52,7 @@
<a class="web-button" href="/init/tickets">
<span class="text">Get my ticket</span>
</a>
<button class="web-button is-secondary" on:click={copy}>
<button class="web-button is-secondary" onclick={copy}>
<span class="web-icon-{$copied ? 'check' : 'copy'} text-primary"></span>
<span class="text">Copy ticket URL</span>

View File

@@ -10,25 +10,27 @@
TicketDetails
} from '$routes/init/(components)/ticket/index.js';
export let data;
let { data } = $props();
let originalName = data.ticket?.name ?? '';
let name = originalName;
let name = $state(originalName);
let originalTitle = data.ticket?.title ?? '';
let title = originalTitle;
let title = $state(originalTitle);
let originalShowGitHub = data.ticket?.show_contributions ?? true;
let showGitHub = originalShowGitHub;
let showGitHub = $state(originalShowGitHub);
let customizing = false;
let saving = false;
let customizing = $state(false);
let saving = $state(false);
$: modified = !dequal(
{
name: originalName,
title: originalTitle,
showGitHub: originalShowGitHub
},
{ name, title, showGitHub }
let modified = $derived(
!dequal(
{
name: originalName,
title: originalTitle,
showGitHub: originalShowGitHub
},
{ name, title, showGitHub }
)
);
async function saveTicket() {

View File

@@ -15,7 +15,7 @@
import { writable } from 'svelte/store';
import type { Integration } from './+page';
export let data;
let { data } = $props();
const title = 'Integrations' + TITLE_SUFFIX;
const description =
@@ -29,26 +29,22 @@
distance: 500
};
let result: ResultType<Integration> = [];
let result: ResultType<Integration> = $state([]);
let hasQuery: boolean;
let query = writable(decodeURIComponent($page.url.searchParams.get('search') ?? ''));
$: query.subscribe((value) => {
hasQuery = value.length > 0;
});
let query = $state(decodeURIComponent($page.url.searchParams.get('search') ?? ''));
let hasQuery = $derived(query.length > 0);
// platform filters
const platforms = ['All', ...data.platforms];
let activePlatform = 'All';
let activePlatform = $state('All');
// categories
let activeCategory: string | null = null;
let activeCategory: string | null = $state(null);
const handleQuery = (e: Event) => {
const value = (e.currentTarget as HTMLInputElement).value;
query.set(value);
query = value;
};
onMount(() => {
@@ -78,7 +74,7 @@
</svelte:head>
<!-- binding for fuse -->
<Fuse list={data.list} options={fuseOptions} bind:query={$query} bind:result />
<Fuse list={data.list} options={fuseOptions} bind:query bind:result />
<Main>
<header class="web-u-sep-block-end web-u-padding-block-end-0 relative overflow-hidden pb-0">
<div class="hero web-u-padding-block-end-0 relative container">
@@ -136,11 +132,13 @@
label="Search"
name="search"
placeholder="Search"
bind:value={$query}
bind:value={query}
autocomplete="off"
on:input={handleQuery}
oninput={handleQuery}
>
<span class="web-icon-search" aria-hidden="true" slot="icon"></span>
{#snippet icon()}
<span class="web-icon-search" aria-hidden="true"></span>
{/snippet}
</Input>
</section>
<section class="flex flex-col">
@@ -162,7 +160,7 @@
}
)}
class:active-tag={activePlatform === platform}
on:click={() => (activePlatform = platform)}
onclick={() => (activePlatform = platform)}
>{platform}</button
>
</li>
@@ -181,7 +179,7 @@
<select
class="web-input-text w-full appearance-none"
disabled={hasQuery}
on:change={(e) =>
onchange={(e) =>
goto(`#${e.currentTarget.value.toLowerCase()}`)}
>
{#each data.categories as category}
@@ -213,8 +211,7 @@
href={`#${category.slug}`}
class="web-link"
class:is-pink={category.slug === activeCategory}
on:click={() =>
activeCategory === category.slug}
onclick={() => activeCategory === category.slug}
>{category.heading}</a
>
</li>
@@ -233,7 +230,7 @@
<h2 class="text-label text-primary">Search results</h2>
<p class="text-description">
{result.length > 0 ? result.length : 'No'} results found
for "{$query}"
for "{query}"
</p>
</header>
<div class="l-max-size-list-cards flex flex-col gap-8">
@@ -531,7 +528,7 @@
scroll-margin-top: f.pxToRem(120);
}
.l-max-size-list-cards {
&:where(:has(> ul > li:nth-child(10))) {
&:where(:global(:has(> ul > li:nth-child(10)))) {
position: relative;
&::before {
@@ -560,7 +557,7 @@
}
}
:where(:target) {
:where(:global(:target)) {
.l-max-size-list-cards {
overflow: visible;
max-block-size: none;

View File

@@ -636,9 +636,9 @@
class="icon-info"
aria-hidden="true"
></button>
<svelte:fragment slot="tooltip">
{#snippet tooltip()}
{row.info}
</svelte:fragment>
{/snippet}
</Tooltip>
{/if}
</div>

View File

@@ -26,8 +26,10 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText = CopyStatus.Copy;
let copyText: CopyStatusValue = CopyStatus.Copy;
async function handleCopy() {
await copy(content);
@@ -77,9 +79,9 @@
aria-label="copy code from code-snippet"
><span class="web-icon-copy" aria-hidden="true"></span></button
>
<svelte:fragment slot="tooltip">
{#snippet tooltip()}
{copyText}
</svelte:fragment>
{/snippet}
</Tooltip>
</li>
</ul>

View File

@@ -170,7 +170,9 @@ async function getLoggedInUser(request) {
/>
</a>
</li>
<svelte:fragment slot="tooltip">{platform.name}</svelte:fragment>
{#snippet tooltip()}
{platform.name}
{/snippet}
</Tooltip>
{/each}
</ul>

View File

@@ -26,8 +26,10 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText = CopyStatus.Copy;
let copyText: CopyStatusValue = CopyStatus.Copy;
async function handleCopy() {
await copy(content);
@@ -77,9 +79,11 @@
aria-label="copy code from code-snippet"
><span class="web-icon-copy" aria-hidden="true"></span></button
>
<svelte:fragment slot="tooltip">
{copyText}
</svelte:fragment>
{#snippet tooltip()}
<span>
{copyText}
</span>
{/snippet}
</Tooltip>
</li>
</ul>

View File

@@ -108,9 +108,9 @@
+{hiddenRuntimes.length}
</span>
</li>
<svelte:fragment slot="tooltip">
{#snippet tooltip()}
<span class="text-micro">{hiddenRuntimes.join(', ')}</span>
</svelte:fragment>
{/snippet}
</Tooltip>
{/if}
</ul>

View File

@@ -26,7 +26,10 @@
Copy: 'Copy',
Copied: 'Copied!'
} as const;
let copyText = CopyStatus.Copy;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText: CopyStatusValue = CopyStatus.Copy;
async function handleCopy() {
await copy(content);
@@ -75,9 +78,11 @@
aria-label="copy code from code-snippet"
><span class="web-icon-copy" aria-hidden="true"></span></button
>
<svelte:fragment slot="tooltip">
{copyText}
</svelte:fragment>
{#snippet tooltip()}
<span>
{copyText}
</span>
{/snippet}
</Tooltip>
</li>
</ul>

View File

@@ -18,12 +18,12 @@
"Appwrite's Threads page showcases our community interactions on Discord. Join the conversation, ask questions, or assist other members with their issues.";
const ogImage = DEFAULT_HOST + '/images/open-graph/website.png';
export let data;
let { data } = $props();
let threads = data.threads;
let threads = $state(data.threads);
let searching = false; // Do some sick animation
let query = '';
let query = $state('');
const handleSearch = async (value: string) => {
query = value;
@@ -84,7 +84,7 @@
'REST API'
];
let selectedTags: string[] = [];
let selectedTags: string[] = $state([]);
function toggleTag(tag: string) {
if (selectedTags.includes(tag)) {
@@ -149,7 +149,7 @@
<button
class="web-btn-tag"
class:is-selected={selectedTags?.includes(tag)}
on:click={() => toggleTag(tag)}
onclick={() => toggleTag(tag)}
>
{tag}
</button>
@@ -200,7 +200,7 @@
<span class="text-body font-medium">No support threads found</span>
<button
class="web-button"
on:click={() => {
onclick={() => {
query = '';
handleSearch('');
}}>Clear search</button

View File

@@ -9,7 +9,7 @@
import PreFooter from '../PreFooter.svelte';
import MessageCard from './MessageCard.svelte';
export let data;
let { data } = $props();
const title = data.title + ' - Threads' + TITLE_SUFFIX;
const description = DEFAULT_DESCRIPTION;

View File

@@ -22,7 +22,11 @@
Copied: 'Copied!'
} as const;
let copyText = CopyStatus.Copy;
type CopyStatusType = keyof typeof CopyStatus;
type CopyStatusValue = (typeof CopyStatus)[CopyStatusType];
let copyText: CopyStatusValue = CopyStatus.Copy;
async function handleCopy() {
await copy(text);
@@ -78,19 +82,18 @@
<ul class="buttons-list flex gap-2">
<li class="buttons-list-item ps-5">
<Tooltip>
<button
slot="asChild"
let:trigger
use:melt={trigger}
on:click={handleCopy}
class="web-icon-button"
aria-label="copy code from code-snippet"
>
<span class="web-icon-copy" aria-hidden="true"></span>
</button>
<svelte:fragment slot="tooltip">
{copyText}
</svelte:fragment>
{#snippet asChild(trigger)}
<button
on:click={handleCopy}
class="web-icon-button"
aria-label="copy code from code-snippet"
>
<span class="web-icon-copy" aria-hidden="true"></span>
</button>
{/snippet}
{#snippet tooltip()}
<span>{copyText}</span>
{/snippet}
</Tooltip>
</li>
</ul>

View File

@@ -1,4 +1,3 @@
import { sentrySvelteKit } from '@sentry/sveltekit';
import dynamicImport from 'vite-plugin-dynamic-import';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
@@ -8,13 +7,6 @@ import manifestSRI from 'vite-plugin-manifest-sri';
export default defineConfig({
plugins: [
sentrySvelteKit({
adapter: 'node',
sourceMapsUploadOptions: {
org: 'appwrite',
project: 'website'
}
}),
enhancedImages(),
sveltekit(),
dynamicImport({