prettier updates

This commit is contained in:
Jesse Winton
2024-07-23 16:08:38 -04:00
parent 79f84f74a0
commit 7cd6ca33d0
278 changed files with 6228 additions and 5966 deletions

View File

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

View File

@@ -1,23 +1,23 @@
name: Search Index
on:
push:
branches:
- main
push:
branches:
- main
jobs:
indexing:
runs-on: ubuntu-latest
name: Indexing
steps:
- uses: actions/checkout@v4
with:
repository: 'meilisearch/scrapix'
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
- run: yarn
- run: yarn start -c "$SCRAPIX_CONFIG"
env:
SCRAPIX_CONFIG: ${{ secrets.SCRAPIX_CONFIG }}
indexing:
runs-on: ubuntu-latest
name: Indexing
steps:
- uses: actions/checkout@v4
with:
repository: 'meilisearch/scrapix'
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
- run: yarn
- run: yarn start -c "$SCRAPIX_CONFIG"
env:
SCRAPIX_CONFIG: ${{ secrets.SCRAPIX_CONFIG }}

View File

@@ -1,23 +1,23 @@
name: Mark stale issues
on:
schedule:
- cron: "0 0 * * *" # Midnight Runtime
schedule:
- cron: '0 0 * * *' # Midnight Runtime
jobs:
stale:
runs-on: ubuntu-latest
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
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"
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"
operations-per-run: 100
only-labels: "question"
steps:
- uses: actions/stale@v9
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'
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'
operations-per-run: 100
only-labels: 'question'

View File

@@ -5,7 +5,5 @@
# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
tasks:
- init: pnpm install && pnpm run build
command: pnpm run dev
- init: pnpm install && pnpm run build
command: pnpm run dev

View File

@@ -49,9 +49,9 @@ Create ordered (numbered) and unordered (bulleted) lists using 1., \*, or -.
**Unordered List**:
```md
- Apple
- Banana
- Cherry
- Apple
- Banana
- Cherry
```
#### Links
@@ -129,15 +129,20 @@ Alternatively, use markdoc tables.
```md
{% table %}
* Heading 1
* Heading 2
- Heading 1
- Heading 2
---
* Row 1 Cell 1
* Row 1 Cell 2
- Row 1 Cell 1
- Row 1 Cell 2
---
* Row 2 Cell 1
* Row 2 cell 2
{% /table %}
- Row 2 Cell 1
- Row 2 cell 2
{% /table %}
```
#### Block Quotes
@@ -186,7 +191,9 @@ print('test');
</pre>
#### Sections
Use sections when there is a clear step-by-step format to a page. This is used mainly in journey pages and tutorials.
```md
{% section #featured-products-1 step=1 title="Title" %}
Lorem ipsum dolor sit amet consectetur.
@@ -243,6 +250,7 @@ Available sizes are `s`, `m`, `l` and `xl`. Default: `s`.
```
#### Cards
We use cards when we reference a list of links for navigation
```
@@ -268,6 +276,7 @@ Get started with Appwrite and SvelteKit
```
#### Cards with icons
We use cards when we reference a list of links for navigation, this variation has icons for extra hints visually.
```
@@ -285,6 +294,7 @@ Configure FCM for push notification to Android and Apple devices.
```
#### Accordions
Use accordions to reduce page size and collapse information that's not important when a reader is skilling the page.
```

View File

@@ -60,11 +60,11 @@ doc-548-submit-a-pull-request-section-to-contribution-guide
When `TYPE` can be:
- **feat** - is a new feature
- **doc** - documentation only changes
- **cicd** - changes related to CI/CD system
- **fix** - a bug fix
- **refactor** - code change that neither fixes a bug nor adds a feature
- **feat** - is a new feature
- **doc** - documentation only changes
- **cicd** - changes related to CI/CD system
- **fix** - a bug fix
- **refactor** - code change that neither fixes a bug nor adds a feature
**All PRs must include a commit message with a description of the changes made!**

200
STYLE.md
View File

@@ -6,17 +6,17 @@ Read this document carefully before making PRs to the Appwrite Website repo.
The Appwrite documentation is meant to provide general guidance that's:
- Unopinionated
- Focused on the correct use of Appwrite product
- Includes examples for all relevant and applicable SDKs
- Agnostic to the user's implementation and stack.
- Unopinionated
- Focused on the correct use of Appwrite product
- Includes examples for all relevant and applicable SDKs
- Agnostic to the user's implementation and stack.
Examples of things not fit for docs, and better as a blog or video:
- General programming advice
- Opinionated implementation patterns like MVVM, factory methods, etc.
- Examples that only include a select subset of Appwrite SDKs.
- Examples that do not work for all developers using Appwrite, but specific to Appwrite + technology.
- General programming advice
- Opinionated implementation patterns like MVVM, factory methods, etc.
- Examples that only include a select subset of Appwrite SDKs.
- Examples that do not work for all developers using Appwrite, but specific to Appwrite + technology.
Note that the tutorials and blogs available on the Appwrite blog and docs are meant for these types of information.
@@ -28,39 +28,39 @@ Appwrite's navigation increases in complexity from top down. We expect users to
Introduction Section:
- [Homes](https://appwrite.io/docs)
- [Quick start](https://appwrite.io/docs/quick-start)
- [Tutorial](https://appwrite.io/docs/tutorial)
- [SDKs](https://appwrite.io/docs/sdks)
- [API references](https://appwrite.io/docs/references)
- [Homes](https://appwrite.io/docs)
- [Quick start](https://appwrite.io/docs/quick-start)
- [Tutorial](https://appwrite.io/docs/tutorial)
- [SDKs](https://appwrite.io/docs/sdks)
- [API references](https://appwrite.io/docs/references)
Products section:
- [Auth](https://appwrite.io/docs/products/auth)
- [Databases](https://appwrite.io/docs/products/databases)
- [Functions](https://appwrite.io/docs/products/functions)
- [Storage](https://appwrite.io/docs/products/storage)
- [Messaging](https://appwrite.io/docs/products/messaging)
- [AI](https://appwrite.io/docs/products/ai)
- [Auth](https://appwrite.io/docs/products/auth)
- [Databases](https://appwrite.io/docs/products/databases)
- [Functions](https://appwrite.io/docs/products/functions)
- [Storage](https://appwrite.io/docs/products/storage)
- [Messaging](https://appwrite.io/docs/products/messaging)
- [AI](https://appwrite.io/docs/products/ai)
APIs section:
- [GraphQL](https://appwrite.io/docs/apis/graphql)
- [REST](https://appwrite.io/docs/apis/rest)
- [Realtime](https://appwrite.io/docs/apis/realtime)
- [GraphQL](https://appwrite.io/docs/apis/graphql)
- [REST](https://appwrite.io/docs/apis/rest)
- [Realtime](https://appwrite.io/docs/apis/realtime)
Tooling section:
- [Command Line](https://appwrite.io/docs/command-line)
- [Command center](https://appwrite.io/docs/tooling/command-center)
- [Assistant](https://appwrite.io/docs/tooling/assistant)
- [Command Line](https://appwrite.io/docs/command-line)
- [Command center](https://appwrite.io/docs/tooling/command-center)
- [Assistant](https://appwrite.io/docs/tooling/assistant)
Advanced section:
- [Platform](https://appwrite.io/docs/advanced/platform)
- [Migrations](https://appwrite.io/docs/advanced/migrations)
- [Self-hosting](https://appwrite.io/docs/advanced/self-hosting)
- [Security](https://appwrite.io/docs/advanced/security)
- [Platform](https://appwrite.io/docs/advanced/platform)
- [Migrations](https://appwrite.io/docs/advanced/migrations)
- [Self-hosting](https://appwrite.io/docs/advanced/self-hosting)
- [Security](https://appwrite.io/docs/advanced/security)
Here's the intended purpose and structure of each section.
@@ -70,10 +70,10 @@ This section is focused on introducing what Appwrite is and giving examples to t
Documentation here is focused on a **single flow** which means a single platform/framework + Appwrite.
Content here is not specific to a specific product, but usually covers multiple Appwrite products.
- If your tutorial can be followed in about 15 minutes and fits on one page, write it under quick start
- If you're writing a long piece of documentation that integrates Appwrite with another technology, with lots of details that's opinionated or isn't relevant for all use cases, write it under tutorial. This is similar to "cook book" at other organizations.
- If you have information like helpers and methods that are only on SDKs but not the API, they go under SDK
- API references are generated from source from the appwrite/appwrite repo
- If your tutorial can be followed in about 15 minutes and fits on one page, write it under quick start
- If you're writing a long piece of documentation that integrates Appwrite with another technology, with lots of details that's opinionated or isn't relevant for all use cases, write it under tutorial. This is similar to "cook book" at other organizations.
- If you have information like helpers and methods that are only on SDKs but not the API, they go under SDK
- API references are generated from source from the appwrite/appwrite repo
### Products
@@ -82,17 +82,17 @@ Code examples should cover **all available SDKs**.
Each product page has three main sections
- Introduction
- Overview - Describes at a high level, why you might need this product
- Quick start - Shows the most basic and quickest example to make something happen with a product. Keep it really short.
- Concept
- These pages usually align with sections shown in the product in the Appwrite Console.
- Focused on describing concepts a user should know, but not actions you might take.
- Cover all the details
- Journeys
- These pages focus on common actions and work flows
- Detailed examples that span many concepts
- Like cookbook at other organizations' documentation.
- Introduction
- Overview - Describes at a high level, why you might need this product
- Quick start - Shows the most basic and quickest example to make something happen with a product. Keep it really short.
- Concept
- These pages usually align with sections shown in the product in the Appwrite Console.
- Focused on describing concepts a user should know, but not actions you might take.
- Cover all the details
- Journeys
- These pages focus on common actions and work flows
- Detailed examples that span many concepts
- Like cookbook at other organizations' documentation.
### APIs section
@@ -106,10 +106,10 @@ Describes tools that help you work with Appwrite, but are usually non-essential
For information that's not used commonly during the development cycle.
- Platform: covers concepts that apply to the entire Appwrite Cloud platform, like API keys, rate limits, etc.
- Migrations: covers migrations feature of Appwrite that helps you move data around.
- Security: purely information about measures Appwrite use to ensure security of the platform and data.
- Self-hosting: The Appwrite self-hosted platform is meant to behave identically to Cloud after being configured corrrectly. This section focuses on how to configure Appwrite self-hosted such that it behaves like Cloud.
- Platform: covers concepts that apply to the entire Appwrite Cloud platform, like API keys, rate limits, etc.
- Migrations: covers migrations feature of Appwrite that helps you move data around.
- Security: purely information about measures Appwrite use to ensure security of the platform and data.
- Self-hosting: The Appwrite self-hosted platform is meant to behave identically to Cloud after being configured corrrectly. This section focuses on how to configure Appwrite self-hosted such that it behaves like Cloud.
## Documentation sources
@@ -117,22 +117,22 @@ The Appwrite docs are compiled from different repositories. Here are the signifi
[appwrite/website](https://github.com/appwrite/website):
- Tutorials
- Quick starts
- Product, API, Tooling and Advanced sections
- Tutorials
- Quick starts
- Product, API, Tooling and Advanced sections
[appwrite/appwrite](https://github.com/appwrite/appwrite):
- [API Reference](https://appwrite.io/docs/references) pages
- API specification
- API description
- API endpoint description
- API request parameters
- API response model
- [API Reference](https://appwrite.io/docs/references) pages
- API specification
- API description
- API endpoint description
- API request parameters
- API response model
[appwrite/sdk-generator](https://github.com/appwrite/sdk-generator):
- Generated examples
- Generated examples
## Markdown Style guidelines
@@ -141,10 +141,10 @@ the tone and voice remains consistent.
### Headings
- All titles, headings, buttons, and labels should be written in **sentence case**. If you're not sure what sentence case should look like, check [APA's style guide](https://apastyle.apa.org/style-grammar-guidelines/capitalization/sentence-case) or check with ChatGPT and other LLMs which reliably converts titles to sentence case.
- All headings in a docs page begin with `# Heading` then `## Heading` and `### Heading`. Internally, they're converted to H2 to H4 tags.
- All headings should have an ID label, for example `# Cool heading {% #cool-heading %}` the `#cool-heading` ID will be used to generate the table of contents and add links to the heading.
- Prefer verbs over gerunds, for example, say "Create documents" not "Creating documents".
- All titles, headings, buttons, and labels should be written in **sentence case**. If you're not sure what sentence case should look like, check [APA's style guide](https://apastyle.apa.org/style-grammar-guidelines/capitalization/sentence-case) or check with ChatGPT and other LLMs which reliably converts titles to sentence case.
- All headings in a docs page begin with `# Heading` then `## Heading` and `### Heading`. Internally, they're converted to H2 to H4 tags.
- All headings should have an ID label, for example `# Cool heading {% #cool-heading %}` the `#cool-heading` ID will be used to generate the table of contents and add links to the heading.
- Prefer verbs over gerunds, for example, say "Create documents" not "Creating documents".
### Extended Markdoc components
@@ -152,14 +152,14 @@ Appwrite's documentation uses extended markdown syntax. You can find all of the
### Screenshots
- When contributing upload original screenshots. The Appwrite design team will edit the screenshot to be consistent with other screenshots in the docs.
- Screenshots must be 16:9
- Screnshots should be taken in a 1400 x 900 view port on 3x DPR in browser developer tools.
- Use generic and sensible organization, project, and resource names. Avoid names like `test`, `demo`, or `sdlkfj`.
- All screenshot should be take from a user named Walter O'Brien. You can change the name of your current user by going to your Appwrite Console and clicking the **top right profile icon** > **Your Account** > **Name**.
- Screenshots are stored in the `/images/docs/` folder, in a parent folder that is consistent with the path of the docs that reference the image.
- All screenshots must be both dark and light mode, with `/path/` holding the lightmode version and `/path/dark/` holding the dark mode version.
- Screenshots should be uploaded as un-edited original. Request help from the Appwrite design team to help you edit and refine your photos according to our guidelines.
- When contributing upload original screenshots. The Appwrite design team will edit the screenshot to be consistent with other screenshots in the docs.
- Screenshots must be 16:9
- Screnshots should be taken in a 1400 x 900 view port on 3x DPR in browser developer tools.
- Use generic and sensible organization, project, and resource names. Avoid names like `test`, `demo`, or `sdlkfj`.
- All screenshot should be take from a user named Walter O'Brien. You can change the name of your current user by going to your Appwrite Console and clicking the **top right profile icon** > **Your Account** > **Name**.
- Screenshots are stored in the `/images/docs/` folder, in a parent folder that is consistent with the path of the docs that reference the image.
- All screenshots must be both dark and light mode, with `/path/` holding the lightmode version and `/path/dark/` holding the dark mode version.
- Screenshots should be uploaded as un-edited original. Request help from the Appwrite design team to help you edit and refine your photos according to our guidelines.
```md
{% only_dark %}
@@ -180,17 +180,23 @@ It should contain all the pages and headings with in them, maintainers may reque
```md
# page 1
## heading a
## heading b
...
# page 2
## heading a
## heading b
...
# page 3
## heading a
## heading b
...
...
```
@@ -204,41 +210,41 @@ Split content such that each piece makes sense without reading dependents or exp
### Release prep
- [ ] Add new version to [src/lib/utils/references.ts](src/lib/utils/references.ts)
- [ ] Point Cloud to new version in [src/routes/docs/references/[version]/[platform]/[service]/+page.server.ts](src/routes/docs/references/[version]/[platform]/[service]/+page.server.ts)
- [ ] Update install command in [/workspaces/website/src/routes/docs/advanced/self-hosting/+page.markdoc](/workspaces/website/src/routes/docs/advanced/self-hosting/+page.markdoc)
- [ ] Update events [src/partials/[product]-events.md](src/partials/)
- [ ] Update response code [src/routes/docs/advanced/platform/response-codes/+page.markdoc](src/routes/docs/advanced/platform/response-codes/+page.markdoc)
- [ ] Bump latest SDK versions in SDKs page, quick start, and tutorials
- [ ] Create new sections for new products
- [ ] Create new concept and journey pages for new features
- [ ] Update docs for breaking changes
- [ ] Add new version to [src/lib/utils/references.ts](src/lib/utils/references.ts)
- [ ] Point Cloud to new version in [src/routes/docs/references/[version]/[platform]/[service]/+page.server.ts](src/routes/docs/references/[version]/[platform]/[service]/+page.server.ts)
- [ ] Update install command in [/workspaces/website/src/routes/docs/advanced/self-hosting/+page.markdoc](/workspaces/website/src/routes/docs/advanced/self-hosting/+page.markdoc)
- [ ] Update events [src/partials/[product]-events.md](src/partials/)
- [ ] Update response code [src/routes/docs/advanced/platform/response-codes/+page.markdoc](src/routes/docs/advanced/platform/response-codes/+page.markdoc)
- [ ] Bump latest SDK versions in SDKs page, quick start, and tutorials
- [ ] Create new sections for new products
- [ ] Create new concept and journey pages for new features
- [ ] Update docs for breaking changes
### Documenting a new API
- Add a new .md file describing the new API here: <https://github.com/appwrite/appwrite/tree/main/docs/references>
- Add descriptions for methods and parameters in the controller code: <https://github.com/appwrite/appwrite/tree/main/app/controllers/api>
- Check new response models have meaningful descriptions
- Add a new .md file describing the new API here: <https://github.com/appwrite/appwrite/tree/main/docs/references>
- Add descriptions for methods and parameters in the controller code: <https://github.com/appwrite/appwrite/tree/main/app/controllers/api>
- Check new response models have meaningful descriptions
### Adding a new quickstart
- Copy a quick start from the [src/routes/docs/quick-starts](src/routes/docs/quick-starts) folder.
- Add a new entry and logo to [src/routes/docs/quick-starts/+page.svelte](src/routes/docs/quick-starts/+page.svelte)
- If you need a new logo, contact the Appwrite team to add one to Pink design.
- Update the content of your tutorial. Remember to update the front matter!
- Try to be consistent in both the quickstart's content and format when compared to existing quick starts
- Add the quick start to the footer and front page of Appwrite
- Use sections for steps on your page
- Copy a quick start from the [src/routes/docs/quick-starts](src/routes/docs/quick-starts) folder.
- Add a new entry and logo to [src/routes/docs/quick-starts/+page.svelte](src/routes/docs/quick-starts/+page.svelte)
- If you need a new logo, contact the Appwrite team to add one to Pink design.
- Update the content of your tutorial. Remember to update the front matter!
- Try to be consistent in both the quickstart's content and format when compared to existing quick starts
- Add the quick start to the footer and front page of Appwrite
- Use sections for steps on your page
### Adding a new tutorial
- Copy a tutorial from the [src/routes/docs/tutorials](src/routes/docs/tutorials) folder.
- Update the `+page.ts`'s redirect, for example, the Android tutorial has this: [src/routes/docs/tutorials/android/+page.ts](src/routes/docs/tutorials/android/+page.ts)
- Update [src/routes/docs/tutorials/+page.svelte](src/routes/docs/tutorials/+page.svelte) and add your new tutorial
- Update [src/routes/docs/tutorials/android/+layout.ts](src/routes/docs/tutorials/android/+layout.ts) and add your new tutorial
- Add the content of your tutorial. Keep pages short, separated by a different distinct feature for each step.
- If you need a new logo, contact the Appwrite team to add one to Pink design.
- Add the tutorial to the footer and front page of Appwrite
- Copy a tutorial from the [src/routes/docs/tutorials](src/routes/docs/tutorials) folder.
- Update the `+page.ts`'s redirect, for example, the Android tutorial has this: [src/routes/docs/tutorials/android/+page.ts](src/routes/docs/tutorials/android/+page.ts)
- Update [src/routes/docs/tutorials/+page.svelte](src/routes/docs/tutorials/+page.svelte) and add your new tutorial
- Update [src/routes/docs/tutorials/android/+layout.ts](src/routes/docs/tutorials/android/+layout.ts) and add your new tutorial
- Add the content of your tutorial. Keep pages short, separated by a different distinct feature for each step.
- If you need a new logo, contact the Appwrite team to add one to Pink design.
- Add the tutorial to the footer and front page of Appwrite
## Language and diction
@@ -283,9 +289,11 @@ Like sentences, important information always comes first.
This makes it easier to scan through the page.
✅ Clear, important information such as actions come first
> Store secrets as environment variables in vaults by navigating to **settings** > **security** > **vault**. Your secrets should never be shared. You must ensure data privacy, sharing secrets can compromise security during development.
❌ Unclear, important information is in the middle of the paragraph
> Security is important in development. That's why you should take care to protect secrets. Secrets should be safely stored as a environment variable in a vault. You can find vaults under **settings** > **security** > **vault**. Don't share this with anyone!
If there are multiple important actions or pieces of information, **break up the paragraph**.

View File

@@ -1,58 +1,58 @@
services:
traefik:
image: traefik:2.9
command:
- --log.level=DEBUG
- --api.insecure=true
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`homepage`)
- --accesslog=true
labels:
- traefik.http.routers.traefik.middlewares=traefik-compress
- traefik.http.middlewares.traefik-compress.compress=true
ports:
- 80:80
- 8080:8080
volumes:
# - /letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
- homepage
traefik:
image: traefik:2.9
command:
- --log.level=DEBUG
- --api.insecure=true
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`homepage`)
- --accesslog=true
labels:
- traefik.http.routers.traefik.middlewares=traefik-compress
- traefik.http.middlewares.traefik-compress.compress=true
ports:
- 80:80
- 8080:8080
volumes:
# - /letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
- homepage
homepage:
image: homepage-dev
build:
context: .
args:
- PUBLIC_APPWRITE_PROJECT_INIT_ID=$PUBLIC_APPWRITE_PROJECT_INIT_ID
- PUBLIC_APPWRITE_PROJECT_ID=$PUBLIC_APPWRITE_PROJECT_ID
- PUBLIC_APPWRITE_DB_MAIN_ID=$PUBLIC_APPWRITE_DB_MAIN_ID
- PUBLIC_APPWRITE_COL_THREADS_ID=$PUBLIC_APPWRITE_COL_THREADS_ID
- PUBLIC_APPWRITE_COL_MESSAGES_ID=$PUBLIC_APPWRITE_COL_MESSAGES_ID
- PUBLIC_APPWRITE_FN_TLDR_ID=$PUBLIC_APPWRITE_FN_TLDR_ID
restart: always
networks:
- homepage
labels:
- traefik.enable=true
- traefik.constraint-label-stack=homepage
- traefik.docker.network=appwrite
- traefik.http.middlewares.appwrite_middlewares.compress=true
- traefik.http.services.appwrite_service.loadbalancer.server.port=3000
#http
- traefik.http.routers.appwrite.entrypoints=web
- traefik.http.routers.appwrite.rule=PathPrefix(`/`)
- traefik.http.routers.appwrite.service=appwrite_service
- traefik.http.routers.appwrite.middlewares=appwrite_middlewares
# https
- traefik.http.routers.appwrite_secure.entrypoints=websecure
- traefik.http.routers.appwrite_secure.rule=PathPrefix(`/`)
- traefik.http.routers.appwrite_secure.service=appwrite_service
- traefik.http.routers.appwrite_secure.tls=true
- traefik.http.routers.appwrite_secure.middlewares=appwrite_middlewares
homepage:
image: homepage-dev
build:
context: .
args:
- PUBLIC_APPWRITE_PROJECT_INIT_ID=$PUBLIC_APPWRITE_PROJECT_INIT_ID
- PUBLIC_APPWRITE_PROJECT_ID=$PUBLIC_APPWRITE_PROJECT_ID
- PUBLIC_APPWRITE_DB_MAIN_ID=$PUBLIC_APPWRITE_DB_MAIN_ID
- PUBLIC_APPWRITE_COL_THREADS_ID=$PUBLIC_APPWRITE_COL_THREADS_ID
- PUBLIC_APPWRITE_COL_MESSAGES_ID=$PUBLIC_APPWRITE_COL_MESSAGES_ID
- PUBLIC_APPWRITE_FN_TLDR_ID=$PUBLIC_APPWRITE_FN_TLDR_ID
restart: always
networks:
- homepage
labels:
- traefik.enable=true
- traefik.constraint-label-stack=homepage
- traefik.docker.network=appwrite
- traefik.http.middlewares.appwrite_middlewares.compress=true
- traefik.http.services.appwrite_service.loadbalancer.server.port=3000
#http
- traefik.http.routers.appwrite.entrypoints=web
- traefik.http.routers.appwrite.rule=PathPrefix(`/`)
- traefik.http.routers.appwrite.service=appwrite_service
- traefik.http.routers.appwrite.middlewares=appwrite_middlewares
# https
- traefik.http.routers.appwrite_secure.entrypoints=websecure
- traefik.http.routers.appwrite_secure.rule=PathPrefix(`/`)
- traefik.http.routers.appwrite_secure.service=appwrite_service
- traefik.http.routers.appwrite_secure.tls=true
- traefik.http.routers.appwrite_secure.middlewares=appwrite_middlewares
networks:
homepage:
homepage:

View File

@@ -1,135 +1,135 @@
x-logging: &x-logging
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '20m'
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '20m'
x-update-config: &x-update-config
update_config:
order: stop-first
failure_action: rollback
parallelism: 1
delay: 10s
rollback_config:
failure_action: pause
monitor: 5s
parallelism: 2
order: stop-first
update_config:
order: stop-first
failure_action: rollback
parallelism: 1
delay: 10s
rollback_config:
failure_action: pause
monitor: 5s
parallelism: 2
order: stop-first
version: '3.8'
services:
traefik:
image: traefik:2.9
<<: *x-logging
command:
- --log.level=DEBUG
- --api.insecure=false
- --providers.docker=true
- --providers.docker.watch=true
- --providers.docker.swarmMode=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`)
- --certificatesresolvers.myresolver.acme.httpchallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.email=$_APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/${_APP_DOMAIN}.json
- --accesslog=true
ports:
- 80:80
- 443:443
volumes:
- /letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
- cloud
deploy:
replicas: 3
<<: *x-update-config
placement:
max_replicas_per_node: 1
constraints:
- node.role == manager
preferences:
- spread: node.role == worker
labels:
- traefik.http.routers.traefik.middlewares=traefik-compress
- traefik.http.middlewares.traefik-compress.compress=true
traefik:
image: traefik:2.9
<<: *x-logging
command:
- --log.level=DEBUG
- --api.insecure=false
- --providers.docker=true
- --providers.docker.watch=true
- --providers.docker.swarmMode=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`)
- --certificatesresolvers.myresolver.acme.httpchallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.email=$_APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/${_APP_DOMAIN}.json
- --accesslog=true
ports:
- 80:80
- 443:443
volumes:
- /letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
- cloud
deploy:
replicas: 3
<<: *x-update-config
placement:
max_replicas_per_node: 1
constraints:
- node.role == manager
preferences:
- spread: node.role == worker
labels:
- traefik.http.routers.traefik.middlewares=traefik-compress
- traefik.http.middlewares.traefik-compress.compress=true
server:
image: ghcr.io/appwrite/website:$_APP_VERSION
<<: *x-logging
networks:
- cloud
environment:
- PUBLIC_APPWRITE_PROJECT_INIT_ID
- PUBLIC_APPWRITE_PROJECT_ID
- PUBLIC_APPWRITE_DB_MAIN_ID
- PUBLIC_APPWRITE_COL_THREADS_ID
- PUBLIC_APPWRITE_COL_MESSAGES_ID
- PUBLIC_APPWRITE_FN_TLDR_ID
deploy:
<<: *x-update-config
mode: replicated
replicas: 8
placement:
max_replicas_per_node: 2
constraints:
- node.role == worker
preferences:
- spread: node.role == worker
labels:
- traefik.enable=true
- traefik.docker.lbswarm=true
- traefik.constraint-label-stack=appwrite
- traefik.http.services.appwrite_service.loadbalancer.server.port=3000
- traefik.http.middlewares.appwrite_middlewares.compress=true
#http
- traefik.http.routers.appwrite.entrypoints=web
- traefik.http.routers.appwrite.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite.service=appwrite_service
- traefik.http.routers.appwrite.middlewares=appwrite_middlewares
# https
- traefik.http.routers.appwrite_secure.entrypoints=websecure
- traefik.http.routers.appwrite_secure.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite_secure.service=appwrite_service
- traefik.http.routers.appwrite_secure.tls=true
- traefik.http.routers.appwrite_secure.tls.certresolver=myresolver
- traefik.http.routers.appwrite_secure.middlewares=appwrite_middlewares
server:
image: ghcr.io/appwrite/website:$_APP_VERSION
<<: *x-logging
networks:
- cloud
environment:
- PUBLIC_APPWRITE_PROJECT_INIT_ID
- PUBLIC_APPWRITE_PROJECT_ID
- PUBLIC_APPWRITE_DB_MAIN_ID
- PUBLIC_APPWRITE_COL_THREADS_ID
- PUBLIC_APPWRITE_COL_MESSAGES_ID
- PUBLIC_APPWRITE_FN_TLDR_ID
deploy:
<<: *x-update-config
mode: replicated
replicas: 8
placement:
max_replicas_per_node: 2
constraints:
- node.role == worker
preferences:
- spread: node.role == worker
labels:
- traefik.enable=true
- traefik.docker.lbswarm=true
- traefik.constraint-label-stack=appwrite
- traefik.http.services.appwrite_service.loadbalancer.server.port=3000
- traefik.http.middlewares.appwrite_middlewares.compress=true
#http
- traefik.http.routers.appwrite.entrypoints=web
- traefik.http.routers.appwrite.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite.service=appwrite_service
- traefik.http.routers.appwrite.middlewares=appwrite_middlewares
# https
- traefik.http.routers.appwrite_secure.entrypoints=websecure
- traefik.http.routers.appwrite_secure.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite_secure.service=appwrite_service
- traefik.http.routers.appwrite_secure.tls=true
- traefik.http.routers.appwrite_secure.tls.certresolver=myresolver
- traefik.http.routers.appwrite_secure.middlewares=appwrite_middlewares
janitor:
image: appwrite/docker-janitor
deploy:
mode: global
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TIME_BETWEEN_RUNS=3600
- UNUSED_TIME=6h
janitor:
image: appwrite/docker-janitor
deploy:
mode: global
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TIME_BETWEEN_RUNS=3600
- UNUSED_TIME=6h
sematext-agent:
image: sematext/agent:latest
environment:
REGION: EU
INFRA_TOKEN: $SEMATEXT_TOKEN
deploy:
mode: global
restart_policy:
condition: any
volumes:
- /:/hostfs:ro
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
- /sys:/host/sys:ro
- /dev:/hostfs/dev:ro
- /var/run:/var/run
- /sys/kernel/debug:/sys/kernel/debug
sematext-agent:
image: sematext/agent:latest
environment:
REGION: EU
INFRA_TOKEN: $SEMATEXT_TOKEN
deploy:
mode: global
restart_policy:
condition: any
volumes:
- /:/hostfs:ro
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
- /sys:/host/sys:ro
- /dev:/hostfs/dev:ro
- /var/run:/var/run
- /sys/kernel/debug:/sys/kernel/debug
networks:
cloud:
driver: overlay
cloud:
driver: overlay

View File

@@ -1,137 +1,137 @@
x-logging: &x-logging
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '20m'
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '20m'
x-update-config: &x-update-config
update_config:
order: stop-first
failure_action: rollback
parallelism: 1
delay: 10s
rollback_config:
failure_action: pause
monitor: 5s
parallelism: 2
order: stop-first
update_config:
order: stop-first
failure_action: rollback
parallelism: 1
delay: 10s
rollback_config:
failure_action: pause
monitor: 5s
parallelism: 2
order: stop-first
version: '3.8'
services:
traefik:
image: traefik:2.9
<<: *x-logging
command:
- --log.level=DEBUG
- --api.insecure=true
- --providers.docker=true
- --providers.docker.watch=true
- --providers.docker.swarmMode=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`)
- --certificatesresolvers.myresolver.acme.httpchallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.email=$_APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/${_APP_DOMAIN}.json
- --accesslog=true
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- /letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
- cloud
deploy:
replicas: 3
<<: *x-update-config
placement:
max_replicas_per_node: 1
constraints:
- node.role == manager
preferences:
- spread: node.role == worker
labels:
- traefik.http.routers.traefik.middlewares=traefik-compress
- traefik.http.middlewares.traefik-compress.compress=true
traefik:
image: traefik:2.9
<<: *x-logging
command:
- --log.level=DEBUG
- --api.insecure=true
- --providers.docker=true
- --providers.docker.watch=true
- --providers.docker.swarmMode=true
- --providers.docker.exposedByDefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`)
- --certificatesresolvers.myresolver.acme.httpchallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.email=$_APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/${_APP_DOMAIN}.json
- --accesslog=true
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- /letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock
networks:
- cloud
deploy:
replicas: 3
<<: *x-update-config
placement:
max_replicas_per_node: 1
constraints:
- node.role == manager
preferences:
- spread: node.role == worker
labels:
- traefik.http.routers.traefik.middlewares=traefik-compress
- traefik.http.middlewares.traefik-compress.compress=true
server:
image: ghcr.io/appwrite/website:$_APP_VERSION
<<: *x-logging
networks:
- cloud
environment:
- PUBLIC_APPWRITE_PROJECT_INIT_ID
- PUBLIC_APPWRITE_PROJECT_ID
- PUBLIC_APPWRITE_DB_MAIN_ID
- PUBLIC_APPWRITE_COL_THREADS_ID
- PUBLIC_APPWRITE_COL_MESSAGES_ID
- PUBLIC_APPWRITE_FN_TLDR_ID
deploy:
<<: *x-update-config
mode: replicated
replicas: 8
placement:
max_replicas_per_node: 2
constraints:
- node.role == worker
preferences:
- spread: node.role == worker
labels:
- traefik.enable=true
- traefik.docker.lbswarm=true
- traefik.constraint-label-stack=appwrite
- traefik.http.services.appwrite_service.loadbalancer.server.port=3000
- traefik.http.middlewares.appwrite_middlewares.compress=true
#http
- traefik.http.routers.appwrite.entrypoints=web
- traefik.http.routers.appwrite.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite.service=appwrite_service
- traefik.http.routers.appwrite.middlewares=appwrite_middlewares
# https
- traefik.http.routers.appwrite_secure.entrypoints=websecure
- traefik.http.routers.appwrite_secure.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite_secure.service=appwrite_service
- traefik.http.routers.appwrite_secure.tls=true
- traefik.http.routers.appwrite_secure.tls.certresolver=myresolver
- traefik.http.routers.appwrite_secure.middlewares=appwrite_middlewares
server:
image: ghcr.io/appwrite/website:$_APP_VERSION
<<: *x-logging
networks:
- cloud
environment:
- PUBLIC_APPWRITE_PROJECT_INIT_ID
- PUBLIC_APPWRITE_PROJECT_ID
- PUBLIC_APPWRITE_DB_MAIN_ID
- PUBLIC_APPWRITE_COL_THREADS_ID
- PUBLIC_APPWRITE_COL_MESSAGES_ID
- PUBLIC_APPWRITE_FN_TLDR_ID
deploy:
<<: *x-update-config
mode: replicated
replicas: 8
placement:
max_replicas_per_node: 2
constraints:
- node.role == worker
preferences:
- spread: node.role == worker
labels:
- traefik.enable=true
- traefik.docker.lbswarm=true
- traefik.constraint-label-stack=appwrite
- traefik.http.services.appwrite_service.loadbalancer.server.port=3000
- traefik.http.middlewares.appwrite_middlewares.compress=true
#http
- traefik.http.routers.appwrite.entrypoints=web
- traefik.http.routers.appwrite.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite.service=appwrite_service
- traefik.http.routers.appwrite.middlewares=appwrite_middlewares
# https
- traefik.http.routers.appwrite_secure.entrypoints=websecure
- traefik.http.routers.appwrite_secure.rule=Host(`$_APP_DOMAIN`) || Host(`www.$_APP_DOMAIN`)
- traefik.http.routers.appwrite_secure.service=appwrite_service
- traefik.http.routers.appwrite_secure.tls=true
- traefik.http.routers.appwrite_secure.tls.certresolver=myresolver
- traefik.http.routers.appwrite_secure.middlewares=appwrite_middlewares
janitor:
image: appwrite/docker-janitor
deploy:
mode: global
environment:
- TIME_BETWEEN_RUNS=600
- UNUSED_TIME=10m
- RUN_ON_STARTUP=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
janitor:
image: appwrite/docker-janitor
deploy:
mode: global
environment:
- TIME_BETWEEN_RUNS=600
- UNUSED_TIME=10m
- RUN_ON_STARTUP=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
sematext-agent:
image: sematext/agent:latest
environment:
REGION: EU
INFRA_TOKEN: $SEMATEXT_TOKEN
deploy:
mode: global
restart_policy:
condition: any
volumes:
- /:/hostfs:ro
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
- /sys:/host/sys:ro
- /dev:/hostfs/dev:ro
- /var/run:/var/run
- /sys/kernel/debug:/sys/kernel/debug
sematext-agent:
image: sematext/agent:latest
environment:
REGION: EU
INFRA_TOKEN: $SEMATEXT_TOKEN
deploy:
mode: global
restart_policy:
condition: any
volumes:
- /:/hostfs:ro
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
- /sys:/host/sys:ro
- /dev:/hostfs/dev:ro
- /var/run:/var/run
- /sys/kernel/debug:/sys/kernel/debug
networks:
cloud:
driver: overlay
cloud:
driver: overlay

View File

@@ -57,7 +57,7 @@
"openapi-types": "^12.1.3",
"oslllo-svg-fixer": "^3.0.0",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.6.5",
"sass": "^1.77.6",

View File

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

24
pnpm-lock.yaml generated
View File

@@ -109,14 +109,14 @@ importers:
specifier: ^8.4.39
version: 8.4.39
prettier:
specifier: ^3.3.2
version: 3.3.2
specifier: ^3.3.3
version: 3.3.3
prettier-plugin-svelte:
specifier: ^3.2.5
version: 3.2.5(prettier@3.3.2)(svelte@4.2.18)
version: 3.2.5(prettier@3.3.3)(svelte@4.2.18)
prettier-plugin-tailwindcss:
specifier: ^0.6.5
version: 0.6.5(prettier-plugin-svelte@3.2.5(prettier@3.3.2)(svelte@4.2.18))(prettier@3.3.2)
version: 0.6.5(prettier-plugin-svelte@3.2.5(prettier@3.3.3)(svelte@4.2.18))(prettier@3.3.3)
sass:
specifier: ^1.77.6
version: 1.77.6
@@ -3388,8 +3388,8 @@ packages:
prettier-plugin-svelte:
optional: true
prettier@3.3.2:
resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
prettier@3.3.3:
resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
engines: {node: '>=14'}
hasBin: true
@@ -7552,18 +7552,18 @@ snapshots:
prelude-ls@1.2.1: {}
prettier-plugin-svelte@3.2.5(prettier@3.3.2)(svelte@4.2.18):
prettier-plugin-svelte@3.2.5(prettier@3.3.3)(svelte@4.2.18):
dependencies:
prettier: 3.3.2
prettier: 3.3.3
svelte: 4.2.18
prettier-plugin-tailwindcss@0.6.5(prettier-plugin-svelte@3.2.5(prettier@3.3.2)(svelte@4.2.18))(prettier@3.3.2):
prettier-plugin-tailwindcss@0.6.5(prettier-plugin-svelte@3.2.5(prettier@3.3.3)(svelte@4.2.18))(prettier@3.3.3):
dependencies:
prettier: 3.3.2
prettier: 3.3.3
optionalDependencies:
prettier-plugin-svelte: 3.2.5(prettier@3.3.2)(svelte@4.2.18)
prettier-plugin-svelte: 3.2.5(prettier@3.3.3)(svelte@4.2.18)
prettier@3.3.2: {}
prettier@3.3.3: {}
pretty-format@29.7.0:
dependencies:

View File

@@ -4,11 +4,13 @@ const perPage = 100;
const outputFile = `./src/lib/contributors.ts`;
const headers = process.env.GITHUB_TOKEN ? {
Authorization: `token ${process.env.GITHUB_TOKEN}`
} : {}
const headers = process.env.GITHUB_TOKEN
? {
Authorization: `token ${process.env.GITHUB_TOKEN}`
}
: {};
console.log(`using github token: ${!!process.env.GITHUB_TOKEN}`)
console.log(`using github token: ${!!process.env.GITHUB_TOKEN}`);
async function fetchRepositories() {
let page = 1;

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
@@ -24,7 +24,7 @@
if (isDocs) {
const theme = localStorage.getItem('theme');
if (theme) {
document.body.classList.remove('theme-dark', 'theme-light');
document.body.classList.remove('theme-dark', 'light');
if (theme === 'system') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
.matches

View File

@@ -18,11 +18,13 @@ Sentry.init({
replaysOnErrorSampleRate: 1.0,
// If you don't want to use Session Replay, just remove the line below:
integrations: [replayIntegration({
maskAllInputs: true,
maskAllText: false,
blockAllMedia: false,
})]
integrations: [
replayIntegration({
maskAllInputs: true,
maskAllText: false,
blockAllMedia: false
})
]
});
// If you have a custom error handler, pass it to `handleErrorWithSentry`

View File

@@ -10,7 +10,7 @@ Sentry.init({
dsn: SENTRY_DSN,
tracesSampleRate: 1,
allowUrls: [/appwrite\.io/]
})
});
const redirectMap = new Map(redirects.map(({ link, redirect }) => [link, redirect]));
@@ -36,4 +36,4 @@ const bannerRewriter: Handle = async ({ event, resolve }) => {
};
export const handle = sequence(Sentry.sentryHandle(), redirecter, bannerRewriter);
export const handleError = Sentry.handleErrorWithSentry();
export const handleError = Sentry.handleErrorWithSentry();

View File

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

View File

@@ -1,302 +1,302 @@
{
"apple": {
"encodedCode": "\\ea01",
"prefix": "web-icon",
"className": "web-icon-apple",
"unicode": "&#59905;"
},
"appwrite": {
"encodedCode": "\\ea02",
"prefix": "web-icon",
"className": "web-icon-appwrite",
"unicode": "&#59906;"
},
"arrow-down": {
"encodedCode": "\\ea03",
"prefix": "web-icon",
"className": "web-icon-arrow-down",
"unicode": "&#59907;"
},
"arrow-ext-link": {
"encodedCode": "\\ea04",
"prefix": "web-icon",
"className": "web-icon-arrow-ext-link",
"unicode": "&#59908;"
},
"arrow-left": {
"encodedCode": "\\ea05",
"prefix": "web-icon",
"className": "web-icon-arrow-left",
"unicode": "&#59909;"
},
"arrow-right": {
"encodedCode": "\\ea06",
"prefix": "web-icon",
"className": "web-icon-arrow-right",
"unicode": "&#59910;"
},
"arrow-up": {
"encodedCode": "\\ea07",
"prefix": "web-icon",
"className": "web-icon-arrow-up",
"unicode": "&#59911;"
},
"calendar": {
"encodedCode": "\\ea08",
"prefix": "web-icon",
"className": "web-icon-calendar",
"unicode": "&#59912;"
},
"check": {
"encodedCode": "\\ea09",
"prefix": "web-icon",
"className": "web-icon-check",
"unicode": "&#59913;"
},
"chevron-down": {
"encodedCode": "\\ea0a",
"prefix": "web-icon",
"className": "web-icon-chevron-down",
"unicode": "&#59914;"
},
"chevron-left": {
"encodedCode": "\\ea0b",
"prefix": "web-icon",
"className": "web-icon-chevron-left",
"unicode": "&#59915;"
},
"chevron-right": {
"encodedCode": "\\ea0c",
"prefix": "web-icon",
"className": "web-icon-chevron-right",
"unicode": "&#59916;"
},
"chevron-up": {
"encodedCode": "\\ea0d",
"prefix": "web-icon",
"className": "web-icon-chevron-up",
"unicode": "&#59917;"
},
"close": {
"encodedCode": "\\ea0e",
"prefix": "web-icon",
"className": "web-icon-close",
"unicode": "&#59918;"
},
"command": {
"encodedCode": "\\ea0f",
"prefix": "web-icon",
"className": "web-icon-command",
"unicode": "&#59919;"
},
"copy": {
"encodedCode": "\\ea10",
"prefix": "web-icon",
"className": "web-icon-copy",
"unicode": "&#59920;"
},
"dark": {
"encodedCode": "\\ea11",
"prefix": "web-icon",
"className": "web-icon-dark",
"unicode": "&#59921;"
},
"discord": {
"encodedCode": "\\ea12",
"prefix": "web-icon",
"className": "web-icon-discord",
"unicode": "&#59922;"
},
"divider-vertical": {
"encodedCode": "\\ea13",
"prefix": "web-icon",
"className": "web-icon-divider-vertical",
"unicode": "&#59923;"
},
"download": {
"encodedCode": "\\ea14",
"prefix": "web-icon",
"className": "web-icon-download",
"unicode": "&#59924;"
},
"ext-link": {
"encodedCode": "\\ea15",
"prefix": "web-icon",
"className": "web-icon-ext-link",
"unicode": "&#59925;"
},
"firebase": {
"encodedCode": "\\ea16",
"prefix": "web-icon",
"className": "web-icon-firebase",
"unicode": "&#59926;"
},
"github": {
"encodedCode": "\\ea17",
"prefix": "web-icon",
"className": "web-icon-github",
"unicode": "&#59927;"
},
"google": {
"encodedCode": "\\ea18",
"prefix": "web-icon",
"className": "web-icon-google",
"unicode": "&#59928;"
},
"hamburger-menu": {
"encodedCode": "\\ea19",
"prefix": "web-icon",
"className": "web-icon-hamburger-menu",
"unicode": "&#59929;"
},
"light": {
"encodedCode": "\\ea1a",
"prefix": "web-icon",
"className": "web-icon-light",
"unicode": "&#59930;"
},
"linkedin": {
"encodedCode": "\\ea1b",
"prefix": "web-icon",
"className": "web-icon-linkedin",
"unicode": "&#59931;"
},
"location": {
"encodedCode": "\\ea1c",
"prefix": "web-icon",
"className": "web-icon-location",
"unicode": "&#59932;"
},
"logout-left": {
"encodedCode": "\\ea1d",
"prefix": "web-icon",
"className": "web-icon-logout-left",
"unicode": "&#59933;"
},
"logout-right": {
"encodedCode": "\\ea1e",
"prefix": "web-icon",
"className": "web-icon-logout-right",
"unicode": "&#59934;"
},
"mailgun": {
"encodedCode": "\\ea1f",
"prefix": "web-icon",
"className": "web-icon-mailgun",
"unicode": "&#59935;"
},
"message": {
"encodedCode": "\\ea20",
"prefix": "web-icon",
"className": "web-icon-message",
"unicode": "&#59936;"
},
"microsoft": {
"encodedCode": "\\ea21",
"prefix": "web-icon",
"className": "web-icon-microsoft",
"unicode": "&#59937;"
},
"minus": {
"encodedCode": "\\ea22",
"prefix": "web-icon",
"className": "web-icon-minus",
"unicode": "&#59938;"
},
"nuxt": {
"encodedCode": "\\ea23",
"prefix": "web-icon",
"className": "web-icon-nuxt",
"unicode": "&#59939;"
},
"platform": {
"encodedCode": "\\ea24",
"prefix": "web-icon",
"className": "web-icon-platform",
"unicode": "&#59940;"
},
"play": {
"encodedCode": "\\ea25",
"prefix": "web-icon",
"className": "web-icon-play",
"unicode": "&#59941;"
},
"plus": {
"encodedCode": "\\ea26",
"prefix": "web-icon",
"className": "web-icon-plus",
"unicode": "&#59942;"
},
"product-hunt": {
"encodedCode": "\\ea27",
"prefix": "web-icon",
"className": "web-icon-product-hunt",
"unicode": "&#59943;"
},
"refine": {
"encodedCode": "\\ea28",
"prefix": "web-icon",
"className": "web-icon-refine",
"unicode": "&#59944;"
},
"rest": {
"encodedCode": "\\ea29",
"prefix": "web-icon",
"className": "web-icon-rest",
"unicode": "&#59945;"
},
"search": {
"encodedCode": "\\ea2a",
"prefix": "web-icon",
"className": "web-icon-search",
"unicode": "&#59946;"
},
"sendgrid": {
"encodedCode": "\\ea2b",
"prefix": "web-icon",
"className": "web-icon-sendgrid",
"unicode": "&#59947;"
},
"star": {
"encodedCode": "\\ea2c",
"prefix": "web-icon",
"className": "web-icon-star",
"unicode": "&#59948;"
},
"system": {
"encodedCode": "\\ea2d",
"prefix": "web-icon",
"className": "web-icon-system",
"unicode": "&#59949;"
},
"textmagic": {
"encodedCode": "\\ea2e",
"prefix": "web-icon",
"className": "web-icon-textmagic",
"unicode": "&#59950;"
},
"twitter": {
"encodedCode": "\\ea2f",
"prefix": "web-icon",
"className": "web-icon-twitter",
"unicode": "&#59951;"
},
"vue": {
"encodedCode": "\\ea30",
"prefix": "web-icon",
"className": "web-icon-vue",
"unicode": "&#59952;"
},
"x": {
"encodedCode": "\\ea31",
"prefix": "web-icon",
"className": "web-icon-x",
"unicode": "&#59953;"
},
"youtube": {
"encodedCode": "\\ea32",
"prefix": "web-icon",
"className": "web-icon-youtube",
"unicode": "&#59954;"
}
"apple": {
"encodedCode": "\\ea01",
"prefix": "web-icon",
"className": "web-icon-apple",
"unicode": "&#59905;"
},
"appwrite": {
"encodedCode": "\\ea02",
"prefix": "web-icon",
"className": "web-icon-appwrite",
"unicode": "&#59906;"
},
"arrow-down": {
"encodedCode": "\\ea03",
"prefix": "web-icon",
"className": "web-icon-arrow-down",
"unicode": "&#59907;"
},
"arrow-ext-link": {
"encodedCode": "\\ea04",
"prefix": "web-icon",
"className": "web-icon-arrow-ext-link",
"unicode": "&#59908;"
},
"arrow-left": {
"encodedCode": "\\ea05",
"prefix": "web-icon",
"className": "web-icon-arrow-left",
"unicode": "&#59909;"
},
"arrow-right": {
"encodedCode": "\\ea06",
"prefix": "web-icon",
"className": "web-icon-arrow-right",
"unicode": "&#59910;"
},
"arrow-up": {
"encodedCode": "\\ea07",
"prefix": "web-icon",
"className": "web-icon-arrow-up",
"unicode": "&#59911;"
},
"calendar": {
"encodedCode": "\\ea08",
"prefix": "web-icon",
"className": "web-icon-calendar",
"unicode": "&#59912;"
},
"check": {
"encodedCode": "\\ea09",
"prefix": "web-icon",
"className": "web-icon-check",
"unicode": "&#59913;"
},
"chevron-down": {
"encodedCode": "\\ea0a",
"prefix": "web-icon",
"className": "web-icon-chevron-down",
"unicode": "&#59914;"
},
"chevron-left": {
"encodedCode": "\\ea0b",
"prefix": "web-icon",
"className": "web-icon-chevron-left",
"unicode": "&#59915;"
},
"chevron-right": {
"encodedCode": "\\ea0c",
"prefix": "web-icon",
"className": "web-icon-chevron-right",
"unicode": "&#59916;"
},
"chevron-up": {
"encodedCode": "\\ea0d",
"prefix": "web-icon",
"className": "web-icon-chevron-up",
"unicode": "&#59917;"
},
"close": {
"encodedCode": "\\ea0e",
"prefix": "web-icon",
"className": "web-icon-close",
"unicode": "&#59918;"
},
"command": {
"encodedCode": "\\ea0f",
"prefix": "web-icon",
"className": "web-icon-command",
"unicode": "&#59919;"
},
"copy": {
"encodedCode": "\\ea10",
"prefix": "web-icon",
"className": "web-icon-copy",
"unicode": "&#59920;"
},
"dark": {
"encodedCode": "\\ea11",
"prefix": "web-icon",
"className": "web-icon-dark",
"unicode": "&#59921;"
},
"discord": {
"encodedCode": "\\ea12",
"prefix": "web-icon",
"className": "web-icon-discord",
"unicode": "&#59922;"
},
"divider-vertical": {
"encodedCode": "\\ea13",
"prefix": "web-icon",
"className": "web-icon-divider-vertical",
"unicode": "&#59923;"
},
"download": {
"encodedCode": "\\ea14",
"prefix": "web-icon",
"className": "web-icon-download",
"unicode": "&#59924;"
},
"ext-link": {
"encodedCode": "\\ea15",
"prefix": "web-icon",
"className": "web-icon-ext-link",
"unicode": "&#59925;"
},
"firebase": {
"encodedCode": "\\ea16",
"prefix": "web-icon",
"className": "web-icon-firebase",
"unicode": "&#59926;"
},
"github": {
"encodedCode": "\\ea17",
"prefix": "web-icon",
"className": "web-icon-github",
"unicode": "&#59927;"
},
"google": {
"encodedCode": "\\ea18",
"prefix": "web-icon",
"className": "web-icon-google",
"unicode": "&#59928;"
},
"hamburger-menu": {
"encodedCode": "\\ea19",
"prefix": "web-icon",
"className": "web-icon-hamburger-menu",
"unicode": "&#59929;"
},
"light": {
"encodedCode": "\\ea1a",
"prefix": "web-icon",
"className": "web-icon-light",
"unicode": "&#59930;"
},
"linkedin": {
"encodedCode": "\\ea1b",
"prefix": "web-icon",
"className": "web-icon-linkedin",
"unicode": "&#59931;"
},
"location": {
"encodedCode": "\\ea1c",
"prefix": "web-icon",
"className": "web-icon-location",
"unicode": "&#59932;"
},
"logout-left": {
"encodedCode": "\\ea1d",
"prefix": "web-icon",
"className": "web-icon-logout-left",
"unicode": "&#59933;"
},
"logout-right": {
"encodedCode": "\\ea1e",
"prefix": "web-icon",
"className": "web-icon-logout-right",
"unicode": "&#59934;"
},
"mailgun": {
"encodedCode": "\\ea1f",
"prefix": "web-icon",
"className": "web-icon-mailgun",
"unicode": "&#59935;"
},
"message": {
"encodedCode": "\\ea20",
"prefix": "web-icon",
"className": "web-icon-message",
"unicode": "&#59936;"
},
"microsoft": {
"encodedCode": "\\ea21",
"prefix": "web-icon",
"className": "web-icon-microsoft",
"unicode": "&#59937;"
},
"minus": {
"encodedCode": "\\ea22",
"prefix": "web-icon",
"className": "web-icon-minus",
"unicode": "&#59938;"
},
"nuxt": {
"encodedCode": "\\ea23",
"prefix": "web-icon",
"className": "web-icon-nuxt",
"unicode": "&#59939;"
},
"platform": {
"encodedCode": "\\ea24",
"prefix": "web-icon",
"className": "web-icon-platform",
"unicode": "&#59940;"
},
"play": {
"encodedCode": "\\ea25",
"prefix": "web-icon",
"className": "web-icon-play",
"unicode": "&#59941;"
},
"plus": {
"encodedCode": "\\ea26",
"prefix": "web-icon",
"className": "web-icon-plus",
"unicode": "&#59942;"
},
"product-hunt": {
"encodedCode": "\\ea27",
"prefix": "web-icon",
"className": "web-icon-product-hunt",
"unicode": "&#59943;"
},
"refine": {
"encodedCode": "\\ea28",
"prefix": "web-icon",
"className": "web-icon-refine",
"unicode": "&#59944;"
},
"rest": {
"encodedCode": "\\ea29",
"prefix": "web-icon",
"className": "web-icon-rest",
"unicode": "&#59945;"
},
"search": {
"encodedCode": "\\ea2a",
"prefix": "web-icon",
"className": "web-icon-search",
"unicode": "&#59946;"
},
"sendgrid": {
"encodedCode": "\\ea2b",
"prefix": "web-icon",
"className": "web-icon-sendgrid",
"unicode": "&#59947;"
},
"star": {
"encodedCode": "\\ea2c",
"prefix": "web-icon",
"className": "web-icon-star",
"unicode": "&#59948;"
},
"system": {
"encodedCode": "\\ea2d",
"prefix": "web-icon",
"className": "web-icon-system",
"unicode": "&#59949;"
},
"textmagic": {
"encodedCode": "\\ea2e",
"prefix": "web-icon",
"className": "web-icon-textmagic",
"unicode": "&#59950;"
},
"twitter": {
"encodedCode": "\\ea2f",
"prefix": "web-icon",
"className": "web-icon-twitter",
"unicode": "&#59951;"
},
"vue": {
"encodedCode": "\\ea30",
"prefix": "web-icon",
"className": "web-icon-vue",
"unicode": "&#59952;"
},
"x": {
"encodedCode": "\\ea31",
"prefix": "web-icon",
"className": "web-icon-x",
"unicode": "&#59953;"
},
"youtube": {
"encodedCode": "\\ea32",
"prefix": "web-icon",
"className": "web-icon-youtube",
"unicode": "&#59954;"
}
}

View File

@@ -1,15 +1,18 @@
@font-face {
font-family: "web-icon";
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"),
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- */
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- */
}
[class^="web-icon-"], [class*=" web-icon-"] {
[class^='web-icon-'],
[class*=' web-icon-'] {
font-family: 'web-icon' !important;
font-size: 20px;
font-style: normal;
@@ -17,53 +20,153 @@
-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-dark:before { content: "\ea11"; }
.web-icon-discord:before { content: "\ea12"; }
.web-icon-divider-vertical:before { content: "\ea13"; }
.web-icon-download:before { content: "\ea14"; }
.web-icon-ext-link:before { content: "\ea15"; }
.web-icon-firebase:before { content: "\ea16"; }
.web-icon-github:before { content: "\ea17"; }
.web-icon-google:before { content: "\ea18"; }
.web-icon-hamburger-menu:before { content: "\ea19"; }
.web-icon-light:before { content: "\ea1a"; }
.web-icon-linkedin:before { content: "\ea1b"; }
.web-icon-location:before { content: "\ea1c"; }
.web-icon-logout-left:before { content: "\ea1d"; }
.web-icon-logout-right:before { content: "\ea1e"; }
.web-icon-mailgun:before { content: "\ea1f"; }
.web-icon-message:before { content: "\ea20"; }
.web-icon-microsoft:before { content: "\ea21"; }
.web-icon-minus:before { content: "\ea22"; }
.web-icon-nuxt:before { content: "\ea23"; }
.web-icon-platform:before { content: "\ea24"; }
.web-icon-play:before { content: "\ea25"; }
.web-icon-plus:before { content: "\ea26"; }
.web-icon-product-hunt:before { content: "\ea27"; }
.web-icon-refine:before { content: "\ea28"; }
.web-icon-rest:before { content: "\ea29"; }
.web-icon-search:before { content: "\ea2a"; }
.web-icon-sendgrid:before { content: "\ea2b"; }
.web-icon-star:before { content: "\ea2c"; }
.web-icon-system:before { content: "\ea2d"; }
.web-icon-textmagic:before { content: "\ea2e"; }
.web-icon-twitter:before { content: "\ea2f"; }
.web-icon-vue:before { content: "\ea30"; }
.web-icon-x:before { content: "\ea31"; }
.web-icon-youtube:before { content: "\ea32"; }
.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-dark:before {
content: '\ea11';
}
.web-icon-discord:before {
content: '\ea12';
}
.web-icon-divider-vertical:before {
content: '\ea13';
}
.web-icon-download:before {
content: '\ea14';
}
.web-icon-ext-link:before {
content: '\ea15';
}
.web-icon-firebase:before {
content: '\ea16';
}
.web-icon-github:before {
content: '\ea17';
}
.web-icon-google:before {
content: '\ea18';
}
.web-icon-hamburger-menu:before {
content: '\ea19';
}
.web-icon-light:before {
content: '\ea1a';
}
.web-icon-linkedin:before {
content: '\ea1b';
}
.web-icon-location:before {
content: '\ea1c';
}
.web-icon-logout-left:before {
content: '\ea1d';
}
.web-icon-logout-right:before {
content: '\ea1e';
}
.web-icon-mailgun:before {
content: '\ea1f';
}
.web-icon-message:before {
content: '\ea20';
}
.web-icon-microsoft:before {
content: '\ea21';
}
.web-icon-minus:before {
content: '\ea22';
}
.web-icon-nuxt:before {
content: '\ea23';
}
.web-icon-platform:before {
content: '\ea24';
}
.web-icon-play:before {
content: '\ea25';
}
.web-icon-plus:before {
content: '\ea26';
}
.web-icon-product-hunt:before {
content: '\ea27';
}
.web-icon-refine:before {
content: '\ea28';
}
.web-icon-rest:before {
content: '\ea29';
}
.web-icon-search:before {
content: '\ea2a';
}
.web-icon-sendgrid:before {
content: '\ea2b';
}
.web-icon-star:before {
content: '\ea2c';
}
.web-icon-system:before {
content: '\ea2d';
}
.web-icon-textmagic:before {
content: '\ea2e';
}
.web-icon-twitter:before {
content: '\ea2f';
}
.web-icon-vue:before {
content: '\ea30';
}
.web-icon-x:before {
content: '\ea31';
}
.web-icon-youtube:before {
content: '\ea32';
}

View File

@@ -1,14 +1,14 @@
<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;
export let tab: string;
const {
elements: { content }
} = getTabsContext();
const {
elements: { content }
} = getTabsContext();
</script>
<div use:melt={$content(tab)}>
<slot />
<slot />
</div>

View File

@@ -1,45 +1,45 @@
<script lang="ts" context="module">
const CTX_KEY = 'tabs';
const CTX_KEY = 'tabs';
type Context = Tabs & {
tabs: readonly string[];
};
type Context = Tabs & {
tabs: readonly string[];
};
const setTabsContext = (ctx: Context) => {
setContext(CTX_KEY, ctx);
};
const setTabsContext = (ctx: Context) => {
setContext(CTX_KEY, ctx);
};
export const getTabsContext = () => {
return getContext<Context>(CTX_KEY);
};
export const getTabsContext = () => {
return getContext<Context>(CTX_KEY);
};
</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 tab = tabs[0];
export let tabs: Context['tabs'];
export let tab = tabs[0];
const ctx = createTabs({
defaultValue: tab,
onValueChange({ next }) {
tab = next;
return next;
}
});
$: value.set(tab);
const ctx = createTabs({
defaultValue: tab,
onValueChange({ next }) {
tab = next;
return next;
}
});
$: value.set(tab);
setTabsContext({ ...ctx, tabs });
setTabsContext({ ...ctx, tabs });
const {
elements: { root },
states: { value }
} = ctx;
const {
elements: { root },
states: { value }
} = ctx;
</script>
<div use:melt={$root}>
<slot TabsList={List} TabContent={Content} />
<slot TabsList={List} TabContent={Content} />
</div>

View File

@@ -2,54 +2,54 @@ import { tick } from 'svelte';
import type { Action } from 'svelte/action';
export type PortalConfig =
| { target?: string | HTMLElement | undefined; prepend?: boolean }
| undefined;
| { 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) => {
let targetEl;
let targetEl;
async function update(newConfig: PortalConfig) {
const { target, prepend } = { ...defaults, ...newConfig };
async function update(newConfig: PortalConfig) {
const { target, prepend } = { ...defaults, ...newConfig };
if (typeof target === 'string') {
targetEl = document.querySelector(target);
if (targetEl === null) {
await tick();
targetEl = document.querySelector(target);
}
if (targetEl === null) {
throw new Error(`No element found matching css selector: "${target}"`);
}
} else if (target instanceof HTMLElement) {
targetEl = target;
} else {
throw new TypeError(
`Unknown portal target type: ${
target === null ? 'null' : typeof target
}. Allowed types: string (CSS selector) or HTMLElement.`
);
}
el.dataset.portal = '';
if (prepend) {
targetEl.prepend(el);
} else {
targetEl.appendChild(el);
}
el.hidden = false;
}
if (typeof target === 'string') {
targetEl = document.querySelector(target);
if (targetEl === null) {
await tick();
targetEl = document.querySelector(target);
}
if (targetEl === null) {
throw new Error(`No element found matching css selector: "${target}"`);
}
} else if (target instanceof HTMLElement) {
targetEl = target;
} else {
throw new TypeError(
`Unknown portal target type: ${
target === null ? 'null' : typeof target
}. Allowed types: string (CSS selector) or HTMLElement.`
);
}
el.dataset.portal = '';
if (prepend) {
targetEl.prepend(el);
} else {
targetEl.appendChild(el);
}
el.hidden = false;
}
function destroy() {
el.remove();
}
function destroy() {
el.remove();
}
update(config ?? {});
return {
update,
destroy
};
update(config ?? {});
return {
update,
destroy
};
};

View File

@@ -4,25 +4,25 @@ import type { Writable } from 'svelte/store';
type Args = Writable<DOMRect | null>;
export const rect: Action<HTMLElement, Args> = (node, store) => {
let observer: ResizeObserver | null = null;
const update = (store: Args) => {
observer?.disconnect();
let observer: ResizeObserver | null = null;
const update = (store: Args) => {
observer?.disconnect();
store.set(node.getBoundingClientRect());
store.set(node.getBoundingClientRect());
// Observe resize
observer = new ResizeObserver(() => {
store.set(node.getBoundingClientRect());
});
observer.observe(node);
};
// Observe resize
observer = new ResizeObserver(() => {
store.set(node.getBoundingClientRect());
});
observer.observe(node);
};
update(store);
update(store);
return {
update,
destroy() {
// no-op
}
};
return {
update,
destroy() {
// no-op
}
};
};

View File

@@ -1,34 +1,34 @@
<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);
const bodyRect = writable<DOMRect | null>(null);
</script>
<div class="relative">
<div
class="true-body"
style:width={`${$bodyRect?.width ?? 0}px`}
style:height={`${$bodyRect?.height ?? 0}px`}
/>
<div class="body" use:rect={bodyRect}>
<slot />
</div>
<div
class="true-body"
style:width={`${$bodyRect?.width ?? 0}px`}
style:height={`${$bodyRect?.height ?? 0}px`}
/>
<div class="body" use:rect={bodyRect}>
<slot />
</div>
</div>
<style lang="scss">
.relative {
position: relative;
overflow: hidden;
}
.relative {
position: relative;
overflow: hidden;
}
.body {
position: absolute;
left: 0;
top: 0;
}
.body {
position: absolute;
left: 0;
top: 0;
}
.true-body {
transition: 0.2s ease;
}
.true-body {
transition: 0.2s ease;
}
</style>

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' });
export let content: string;
$: codeHtml = getCodeHtml({ content, language: 'js' });
</script>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->

View File

@@ -1,96 +1,96 @@
<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">
<div class="header">
<div class="ellipse" />
<div class="ellipse-2" />
<div class="ellipse-3" />
</div>
<div class="block">
<AutoBox>
<slot {Code} />
</AutoBox>
</div>
<div id="code-bottom" />
<div class="header">
<div class="ellipse" />
<div class="ellipse-2" />
<div class="ellipse-3" />
</div>
<div class="block">
<AutoBox>
<slot {Code} />
</AutoBox>
</div>
<div id="code-bottom" />
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
.code-console {
@include gradients.border-gradient;
--p-radius: 16px;
.code-console {
@include gradients.border-gradient;
--p-radius: 16px;
display: flex;
flex-direction: column;
display: flex;
flex-direction: column;
background-color: hsl(var(--web-color-card));
border-radius: var(--p-radius);
--m-border-radius: var(--p-radius);
--m-border-gradient-before: linear-gradient(
180deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(255, 255, 255, 0) 125.11%
);
backdrop-filter: blur(8px);
background-color: hsl(var(--web-color-card));
border-radius: var(--p-radius);
--m-border-radius: var(--p-radius);
--m-border-gradient-before: linear-gradient(
180deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(255, 255, 255, 0) 125.11%
);
backdrop-filter: blur(8px);
min-width: 330px;
width: fit-content;
padding: 0 0.25rem 0.25rem;
}
min-width: 330px;
width: fit-content;
padding: 0 0.25rem 0.25rem;
}
.block {
width: 100%;
flex-grow: 1;
text-align: left;
.block {
width: 100%;
flex-grow: 1;
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%);
padding: 20px;
border-radius: 12px;
background: linear-gradient(129deg, rgba(0, 0, 0, 0.48) 22.38%, rgba(0, 0, 0, 0) 136.5%);
padding: 20px;
position: relative;
}
position: relative;
}
.header {
position: relative;
display: flex;
align-items: center;
gap: 0.375rem;
padding: 0.75rem;
.header {
position: relative;
display: flex;
align-items: center;
gap: 0.375rem;
padding: 0.75rem;
.ellipse {
background-color: #ec6a5e;
.ellipse {
background-color: #ec6a5e;
width: 8px;
height: 8px;
top: 12px;
border-radius: 4px;
}
width: 8px;
height: 8px;
top: 12px;
border-radius: 4px;
}
.ellipse-2 {
background-color: #f5bf4f;
.ellipse-2 {
background-color: #f5bf4f;
width: 8px;
height: 8px;
top: 12px;
border-radius: 4px;
}
width: 8px;
height: 8px;
top: 12px;
border-radius: 4px;
}
.ellipse-3 {
background-color: #61c554;
.ellipse-3 {
background-color: #61c554;
width: 8px;
height: 8px;
top: 12px;
border-radius: 4px;
}
}
width: 8px;
height: 8px;
top: 12px;
border-radius: 4px;
}
}
.code-console :global(code) {
white-space: pre;
tab-size: 4;
}
.code-console :global(code) {
white-space: pre;
tab-size: 4;
}
</style>

View File

@@ -182,7 +182,7 @@
class="web-card is-white web-u-min-block-size-320 u-flex-vertical oss-card"
id="oss-discord"
>
<div class="u-flex-vertical u-main-space-between u-gap-32">
<div class="u-flex-vertical u-main-space-between gap-8">
<span
class="web-icon-discord web-u-font-size-40"
aria-hidden="true"
@@ -197,7 +197,7 @@
id="oss-github"
href="https://github.com/appwrite/appwrite"
>
<div class="u-flex-vertical u-main-space-between u-gap-32">
<div class="u-flex-vertical u-main-space-between gap-8">
<span
class="web-icon-github web-u-font-size-40"
aria-hidden="true"
@@ -212,7 +212,7 @@
class="web-card is-white web-u-min-block-size-320 u-flex-vertical oss-card"
id="oss-twitter"
>
<div class="u-flex-vertical u-main-space-between u-gap-32">
<div class="u-flex-vertical u-main-space-between gap-8">
<span
class="web-icon-x web-u-font-size-40"
aria-hidden="true"
@@ -227,7 +227,7 @@
class="web-card is-white web-u-min-block-size-320 u-flex-vertical oss-card"
id="oss-youtube"
>
<div class="u-flex-vertical u-main-space-between u-gap-32">
<div class="u-flex-vertical u-main-space-between gap-8">
<span
class="web-icon-youtube web-u-font-size-40"
aria-hidden="true"
@@ -242,7 +242,7 @@
id="oss-commits"
href="https://github.com/appwrite/appwrite"
>
<div class="u-flex-vertical u-main-space-between u-gap-32">
<div class="u-flex-vertical u-main-space-between gap-8">
<span
class="web-icon-github web-u-font-size-40"
aria-hidden="true"

View File

@@ -1,9 +1,9 @@
<script lang="ts">
export let id: string | undefined = undefined;
export let id: string | undefined = undefined;
</script>
<div class="phone" {id}>
<div class="inner">
<slot />
</div>
<div class="inner">
<slot />
</div>
</div>

View File

@@ -1,55 +1,55 @@
<script lang="ts">
import AutoHeight from '../AutoBox.svelte';
import AutoHeight from '../AutoBox.svelte';
</script>
<div class="anim-box">
<div class="top"><slot name="top" /></div>
<div class="content">
<AutoHeight>
<slot />
</AutoHeight>
</div>
<div class="top"><slot name="top" /></div>
<div class="content">
<AutoHeight>
<slot />
</AutoHeight>
</div>
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
.anim-box {
@include gradients.border-gradient;
--m-border-radius: 1rem;
--m-border-gradient-before: linear-gradient(
180deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(255, 255, 255, 0) 125.11%
);
border-radius: var(--m-border-radius);
background: hsl(var(--web-color-card));
backdrop-filter: blur(8px);
.anim-box {
@include gradients.border-gradient;
--m-border-radius: 1rem;
--m-border-gradient-before: linear-gradient(
180deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(255, 255, 255, 0) 125.11%
);
border-radius: var(--m-border-radius);
background: hsl(var(--web-color-card));
backdrop-filter: blur(8px);
padding: 0.5rem;
padding-block-start: 0;
padding: 0.5rem;
padding-block-start: 0;
text-align: left;
text-align: left;
.top {
color: var(--greyscale-50, #ededf0);
font-family: Aeonik Pro;
font-size: 1.25rem;
font-style: normal;
font-weight: 400;
line-height: 2rem; /* 160% */
letter-spacing: -0.0125rem;
.top {
color: var(--greyscale-50, #ededf0);
font-family: Aeonik Pro;
font-size: 1.25rem;
font-style: normal;
font-weight: 400;
line-height: 2rem; /* 160% */
letter-spacing: -0.0125rem;
padding: 1rem;
text-align: left;
}
padding: 1rem;
text-align: left;
}
.content {
border-radius: 0.75rem;
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(30px);
.content {
border-radius: 0.75rem;
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(30px);
position: relative;
}
}
position: relative;
}
}
</style>

View File

@@ -247,7 +247,7 @@
Your backend, minus the hassle
</h2>
<p
class="web-description web-u-max-width-700 u-margin-inline-auto"
class="web-description web-u-max-width-700 mx-auto"
transition:fly={{
y: 16,
delay: 400

View File

@@ -1,61 +1,61 @@
<script lang="ts">
import { createCheckbox, melt } from '@melt-ui/svelte';
import { createCheckbox, melt } from '@melt-ui/svelte';
export let checked = false;
export let checked = false;
const {
elements: { root },
states: { checked: localChecked },
helpers: { isChecked }
} = createCheckbox({
onCheckedChange({ next }) {
if (typeof next === 'boolean') {
checked = next;
}
return next;
}
});
const {
elements: { root },
states: { checked: localChecked },
helpers: { isChecked }
} = createCheckbox({
onCheckedChange({ next }) {
if (typeof next === 'boolean') {
checked = next;
}
return next;
}
});
$: localChecked.set(checked);
$: localChecked.set(checked);
</script>
<div class="wrapper">
<button use:melt={$root} class="anim-checkbox">
{#if $isChecked}
<span class="web-icon-check" />
{/if}
</button>
<button use:melt={$root} class="anim-checkbox">
{#if $isChecked}
<span class="web-icon-check" />
{/if}
</button>
</div>
<style lang="scss">
.wrapper {
display: grid;
place-items: center;
}
.wrapper {
display: grid;
place-items: center;
}
.anim-checkbox {
width: 1rem;
height: 1rem;
flex-shrink: 0;
.anim-checkbox {
width: 1rem;
height: 1rem;
flex-shrink: 0;
border-radius: 0.125rem;
border: 1.5px solid var(--greyscale-500, #818186);
border-radius: 0.125rem;
border: 1.5px solid var(--greyscale-500, #818186);
position: relative;
position: relative;
&:global(.anim-checkbox[data-state='checked']) {
background-color: #7c67fe;
border-color: #7c67fe;
}
}
&:global(.anim-checkbox[data-state='checked']) {
background-color: #7c67fe;
border-color: #7c67fe;
}
}
[class*='icon-'] {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
[class*='icon-'] {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 1rem;
}
color: white;
font-size: 1rem;
}
</style>

View File

@@ -1,64 +1,64 @@
<script lang="ts">
import { getInitials } from '$lib/animations';
import { fly } from 'svelte/transition';
import { authController } from '.';
import { flip } from '$lib/utils/flip';
import { getInitials } from '$lib/animations';
import { fly } from 'svelte/transition';
import { authController } from '.';
import { flip } from '$lib/utils/flip';
const { state } = authController;
const { state } = authController;
type AuthEntry = {
avatar: string;
name: string;
email: string;
id: number;
};
$: authData = [
$state.submitted
? {
avatar: getInitials($state.name),
name: $state.name,
email: $state.email,
id: 0
}
: undefined,
{
avatar: 'BD',
name: 'Benjamin Davis',
email: 'benjamin.davis@example.com',
id: 1
},
{
avatar: 'OS',
name: 'Olivia Smith',
email: 'olivia.smith@example.com',
id: 2
},
{
avatar: 'EW',
name: 'Ethan Wilson',
email: 'ethan.wilson@example.com',
id: 3
}
].filter(Boolean) as AuthEntry[];
type AuthEntry = {
avatar: string;
name: string;
email: string;
id: number;
};
$: authData = [
$state.submitted
? {
avatar: getInitials($state.name),
name: $state.name,
email: $state.email,
id: 0
}
: undefined,
{
avatar: 'BD',
name: 'Benjamin Davis',
email: 'benjamin.davis@example.com',
id: 1
},
{
avatar: 'OS',
name: 'Olivia Smith',
email: 'olivia.smith@example.com',
id: 2
},
{
avatar: 'EW',
name: 'Ethan Wilson',
email: 'ethan.wilson@example.com',
id: 3
}
].filter(Boolean) as AuthEntry[];
</script>
<div class="pseudo-table">
<div class="header">
<span class="web-eyebrow">Name</span>
<span class="web-eyebrow">Identifier</span>
</div>
{#each authData as user (user.id)}
<div
class="row"
in:fly={{ duration: 100, x: -16, delay: 100 }}
out:fly={{ duration: 100, x: -16 }}
animate:flip={{ duration: 150 }}
>
<div class="u-flex u-cross-center u-gap-12">
<div class="avatar is-size-small">{user.avatar}</div>
<span class="truncated">{user.name}</span>
</div>
<span class="truncated">{user.email}</span>
</div>
{/each}
<div class="header">
<span class="web-eyebrow">Name</span>
<span class="web-eyebrow">Identifier</span>
</div>
{#each authData as user (user.id)}
<div
class="row"
in:fly={{ duration: 100, x: -16, delay: 100 }}
out:fly={{ duration: 100, x: -16 }}
animate:flip={{ duration: 150 }}
>
<div class="flex u-cross-center gap-3">
<div class="avatar is-size-small">{user.avatar}</div>
<span class="truncated">{user.name}</span>
</div>
<span class="truncated">{user.email}</span>
</div>
{/each}
</div>

View File

@@ -1,211 +1,220 @@
<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;
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 theme-light">
<p class="title">Create an Account</p>
<p class="subtitle">Please enter your details</p>
<div class="inputs">
<fieldset>
<label for="name">Your Name</label>
<input type="name" id="name" placeholder="Enter your name" bind:value={$state.name} />
</fieldset>
<fieldset>
<label for="email">Your Email</label>
<input type="email" id="email" placeholder="Enter your email" bind:value={$state.email} />
</fieldset>
<fieldset>
<label for="password">Create Password</label>
<input
type="password"
id="password"
placeholder="Enter Password"
bind:value={$state.password}
/>
</fieldset>
</div>
<button class="sign-up">Sign Up</button>
{#if controlsEnabled}
<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 class="oauth" transition:fade={{ duration: 100 }} animate:flip={{ duration: 250 }}>
<div class="inner">
<span class="web-icon-{provider.toLowerCase()}" />
<span>{provider}</span>
</div>
</button>
{/each}
</div>
{/if}
<div data-theme-ignore class="inner-phone light">
<p class="title">Create an Account</p>
<p class="subtitle">Please enter your details</p>
<div class="inputs">
<fieldset>
<label for="name">Your Name</label>
<input type="name" id="name" placeholder="Enter your name" bind:value={$state.name} />
</fieldset>
<fieldset>
<label for="email">Your Email</label>
<input
type="email"
id="email"
placeholder="Enter your email"
bind:value={$state.email}
/>
</fieldset>
<fieldset>
<label for="password">Create Password</label>
<input
type="password"
id="password"
placeholder="Enter Password"
bind:value={$state.password}
/>
</fieldset>
</div>
<button class="sign-up">Sign Up</button>
{#if controlsEnabled}
<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
class="oauth"
transition:fade={{ duration: 100 }}
animate:flip={{ duration: 250 }}
>
<div class="inner">
<span class="web-icon-{provider.toLowerCase()}" />
<span>{provider}</span>
</div>
</button>
{/each}
</div>
{/if}
</div>
<style lang="scss">
.inner-phone {
padding-block: 3rem;
padding-inline: 1rem;
.inner-phone {
padding-block: 3rem;
padding-inline: 1rem;
color: rgba(67, 67, 71, 1);
text-align: left;
color: rgba(67, 67, 71, 1);
text-align: left;
.title {
color: #434347;
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 22px; /* 137.5% */
letter-spacing: -0.224px;
}
.title {
color: #434347;
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 22px; /* 137.5% */
letter-spacing: -0.224px;
}
.subtitle {
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.196px;
}
.subtitle {
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.196px;
}
.inputs {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-block-start: 1.5rem;
.inputs {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-block-start: 1.5rem;
fieldset {
display: flex;
flex-direction: column;
gap: 0.3125rem;
width: 100%;
fieldset {
display: flex;
flex-direction: column;
gap: 0.3125rem;
width: 100%;
label {
color: var(--color-greyscale-700, #56565c);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px; /* 133.333% */
letter-spacing: -0.168px;
}
label {
color: var(--color-greyscale-700, #56565c);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px; /* 133.333% */
letter-spacing: -0.168px;
}
input {
all: unset;
display: flex;
padding: 8px 12px;
align-items: flex-start;
align-self: stretch;
border-radius: 8px;
border: 1px solid #d8d8db;
input {
all: unset;
display: flex;
padding: 8px 12px;
align-items: flex-start;
align-self: stretch;
border-radius: 8px;
border: 1px solid #d8d8db;
color: #434347;
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px; /* 133.333% */
letter-spacing: -0.168px;
}
}
}
color: #434347;
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px; /* 133.333% */
letter-spacing: -0.168px;
}
}
}
.sign-up {
padding: 0.375rem 0.75rem;
text-align: center;
width: 100%;
margin-block-start: 1.25rem;
.sign-up {
padding: 0.375rem 0.75rem;
text-align: center;
width: 100%;
margin-block-start: 1.25rem;
border-radius: 0.5rem;
background: var(--appwrite-purple, #7c67fe);
box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.06);
border-radius: 0.5rem;
background: var(--appwrite-purple, #7c67fe);
box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.06);
color: var(--color-bw-white, #fff);
text-align: center;
color: var(--color-bw-white, #fff);
text-align: center;
/* Responsive/SubBody-500 */
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 22px; /* 157.143% */
letter-spacing: -0.07px;
}
/* Responsive/SubBody-500 */
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 22px; /* 157.143% */
letter-spacing: -0.07px;
}
.with-sep {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
.with-sep {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 166.667% */
letter-spacing: -0.0105rem;
color: hsl(var(--web-color-greyscale-500));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 166.667% */
letter-spacing: -0.0105rem;
color: hsl(var(--web-color-greyscale-500));
margin-block-start: 0.75rem;
margin-block-start: 0.75rem;
&::before,
&::after {
content: '';
height: 1px;
flex-grow: 1;
background-color: hsl(var(--web-color-greyscale-200));
}
}
&::before,
&::after {
content: '';
height: 1px;
flex-grow: 1;
background-color: hsl(var(--web-color-greyscale-200));
}
}
.oauth-btns {
--gap: 0.5rem;
display: flex;
flex-wrap: wrap;
gap: var(--gap);
margin-block-start: 0.75rem;
}
.oauth-btns {
--gap: 0.5rem;
display: flex;
flex-wrap: wrap;
gap: var(--gap);
margin-block-start: 0.75rem;
}
.oauth {
display: flex;
justify-content: center;
align-items: center;
.oauth {
display: flex;
justify-content: center;
align-items: center;
border-radius: 0.5rem;
border: 1px solid #d9d9d9;
color: hsl(var(--web-color-greyscale-750));
text-align: center;
border-radius: 0.5rem;
border: 1px solid #d9d9d9;
color: hsl(var(--web-color-greyscale-750));
text-align: center;
/* Responsive/Caption-500 */
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.01575rem;
/* Responsive/Caption-500 */
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.01575rem;
flex: 1 1 calc(50% - var(--gap));
padding-block: 0.375rem;
position: relative;
height: 2.125rem;
flex: 1 1 calc(50% - var(--gap));
padding-block: 0.375rem;
position: relative;
height: 2.125rem;
.inner {
position: absolute;
left: 50%;
top: 50%;
.inner {
position: absolute;
left: 50%;
top: 50%;
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
transform: translate(-50%, -50%) scale(var(--inverse-sx, 1), var(--inverse-sy, 1));
}
}
}
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
transform: translate(-50%, -50%) scale(var(--inverse-sx, 1), var(--inverse-sy, 1));
}
}
}
</style>

View File

@@ -1,54 +1,54 @@
<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;
const { state } = databasesController;
</script>
<div class="pseudo-table">
<div class="header">
<span class="web-eyebrow">Document ID</span>
<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="copy-button">
<span class="web-icon-copy" />
<span>{task.id}</span>
</div>
<span class="truncated">{task.title}</span>
</div>
{/each}
<div class="header">
<span class="web-eyebrow">Document ID</span>
<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="copy-button">
<span class="web-icon-copy" />
<span>{task.id}</span>
</div>
<span class="truncated">{task.title}</span>
</div>
{/each}
</div>
<style lang="scss">
.copy-button {
display: flex;
padding: 0.25rem 0.5rem;
align-items: center;
gap: 0.375rem;
.copy-button {
display: flex;
padding: 0.25rem 0.5rem;
align-items: center;
gap: 0.375rem;
border-radius: 62.4375rem;
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(2.6666667461395264px);
border-radius: 62.4375rem;
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(2.6666667461395264px);
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-600));
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-600));
}
span:not([class*='icon-']) {
color: var(--greyscale-400, #adadb1);
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 142.857% */
span:not([class*='icon-']) {
color: var(--greyscale-400, #adadb1);
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 142.857% */
overflow: hidden;
text-overflow: ellipsis;
}
}
overflow: hidden;
text-overflow: ellipsis;
}
}
</style>

View File

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

View File

@@ -7,88 +7,88 @@ import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
type Task = {
id: string;
title: string;
checked: boolean;
id: string;
title: string;
checked: boolean;
};
type State = {
tasks: Task[];
tableSlice: number;
tasks: Task[];
tableSlice: number;
};
const state = createResettable<State>({
tasks: [
{
id: '3397fecdedb13397fecdedb1',
title: 'Research user needs',
checked: true
}
],
tableSlice: 1
tasks: [
{
id: '3397fecdedb13397fecdedb1',
title: 'Research user needs',
checked: true
}
],
tableSlice: 1
});
const execute = async () => {
const phone = getElSelector('phone');
const box = getElSelector('box');
const code = getElSelector('code');
const { update } = state.reset();
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
]);
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
]);
await sleep(250);
await sleep(250);
update((p) => ({
...p,
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb2',
title: 'Create wireframes',
checked: false
}
]
}));
await sleep(250);
update((p) => ({
...p,
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb2',
title: 'Create wireframes',
checked: false
}
]
}));
await sleep(250);
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
}));
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
}));
await sleep(250);
await sleep(250);
update((p) => ({
...p,
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb3',
title: 'Create visual design',
checked: false
}
]
}));
update((p) => ({
...p,
tasks: [
...p.tasks,
{
id: '3397fecdedb13397fecdedb3',
title: 'Create visual design',
checked: false
}
]
}));
await sleep(250);
await sleep(250);
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
}));
update((p) => ({
...p,
tableSlice: p.tableSlice + 1
}));
};
export const databasesController = {
execute,
state
execute,
state
};
export const Databases = {
Phone,
Box,
Code
Phone,
Box,
Code
};

View File

@@ -1,127 +1,127 @@
<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;
const { state } = databasesController;
</script>
<div data-theme-ignore class="inner-phone theme-light">
<div class="header">
<p class="title">Your tasks</p>
<span class="icon-menu" aria-label="menu" />
</div>
<div data-theme-ignore class="inner-phone light">
<div class="header">
<p class="title">Your tasks</p>
<span class="icon-menu" aria-label="menu" />
</div>
<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 }}>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
{/each}
</div>
<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 }}>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
{/each}
</div>
<button class="add-btn">
<span class="web-icon-plus" />
</button>
<button class="add-btn">
<span class="web-icon-plus" />
</button>
</div>
<style lang="scss">
.inner-phone {
padding-block: 3rem;
padding-inline: 1rem;
.inner-phone {
padding-block: 3rem;
padding-inline: 1rem;
color: rgba(67, 67, 71, 1);
text-align: left;
color: rgba(67, 67, 71, 1);
text-align: left;
position: relative;
height: 100%;
position: relative;
height: 100%;
.header {
display: flex;
justify-content: space-between;
align-items: center;
.header {
display: flex;
justify-content: space-between;
align-items: center;
.title {
color: var(--color-greyscale-800, #2d2d31);
font-family: Inter;
font-size: 1rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
.title {
color: var(--color-greyscale-800, #2d2d31);
font-family: Inter;
font-size: 1rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
}
.date {
margin-block-start: 3rem;
.date {
margin-block-start: 3rem;
color: hsl(var(--web-color-greyscale-600));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 500;
line-height: 1.25rem; /* 166.667% */
}
color: hsl(var(--web-color-greyscale-600));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 500;
line-height: 1.25rem; /* 166.667% */
}
.tasks {
margin-block-start: 0.5rem;
.tasks {
margin-block-start: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
.task {
display: flex;
align-items: center;
gap: 0.75rem;
.task {
display: flex;
align-items: center;
gap: 0.75rem;
border-radius: 0.5rem;
border: 1px solid hsl(var(--web-color-greyscale-50));
background: hsl(var(--web-color-white));
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
border-radius: 0.5rem;
border: 1px solid hsl(var(--web-color-greyscale-50));
background: hsl(var(--web-color-white));
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
padding-block: 0.55rem;
padding-inline: 0.88rem;
padding-block: 0.55rem;
padding-inline: 0.88rem;
/* Responsive/SubBody-400 */
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.00394rem;
/* Responsive/SubBody-400 */
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.00394rem;
transition: opacity 200ms ease;
transition: opacity 200ms ease;
&[data-checked] {
opacity: 0.6;
}
}
}
&[data-checked] {
opacity: 0.6;
}
}
}
.add-btn {
position: absolute;
right: 1rem;
bottom: 2.5rem;
.add-btn {
position: absolute;
right: 1rem;
bottom: 2.5rem;
width: 2.5rem;
height: 2.5rem;
flex-shrink: 0;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.12);
background-color: rgba(124, 103, 254, 1);
color: rgba(237, 237, 240, 1);
font-size: 1.5rem;
width: 2.5rem;
height: 2.5rem;
flex-shrink: 0;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.12);
background-color: rgba(124, 103, 254, 1);
color: rgba(237, 237, 240, 1);
font-size: 1.5rem;
display: grid;
place-items: center;
border-radius: 100%;
}
}
display: grid;
place-items: center;
border-radius: 100%;
}
}
</style>

View File

@@ -1,10 +1,10 @@
<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 = `
let content = `
const userId = req.headers['user-id'];
if (req.path === '/subscribe') {
@@ -18,39 +18,39 @@ if (req.path === '/webhook') {
return res.json({ success: true });`.trim();
const { state } = functionsController;
const { state } = functionsController;
</script>
<Code {content} />
<div use:portal={{ target: '#code-bottom' }} class="bottom">
{#if $state.submit !== 'idle'}
<span class="web-icon-github" in:fade />
{/if}
{#if $state.submit === 'loading'}
<span in:fade>Pushing to GitHub...</span>
<div class="loader is-small" in:fade />
{:else if $state.submit === 'success'}
<span>Deployed to Appwrite Cloud</span>
<span class="web-icon-check" />
{/if}
{#if $state.submit !== 'idle'}
<span class="web-icon-github" in:fade />
{/if}
{#if $state.submit === 'loading'}
<span in:fade>Pushing to GitHub...</span>
<div class="loader is-small" in:fade />
{:else if $state.submit === 'success'}
<span>Deployed to Appwrite Cloud</span>
<span class="web-icon-check" />
{/if}
</div>
<style lang="scss">
.bottom {
display: flex;
align-items: center;
gap: 0.5rem;
.bottom {
display: flex;
align-items: center;
gap: 0.5rem;
height: 3rem;
padding-inline: 1rem;
height: 3rem;
padding-inline: 1rem;
color: var(--color-bw-white, #fff);
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.3125rem */
letter-spacing: -0.00875rem;
}
color: var(--color-bw-white, #fff);
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.3125rem */
letter-spacing: -0.00875rem;
}
</style>

View File

@@ -6,53 +6,53 @@ 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();
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
]);
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
]);
await sleep(250);
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, { zIndex: 0 }, { duration: 0 })?.finished;
await safeAnimate(code, { y: [200 - 16, 200], opacity: 1 }, { duration: 0.5 })?.finished;
await sleep(250);
await sleep(250);
update((p) => ({
...p,
submit: 'loading'
}));
update((p) => ({
...p,
submit: 'loading'
}));
await sleep(1500);
await sleep(1500);
update((p) => ({
...p,
submit: 'success'
}));
update((p) => ({
...p,
submit: 'success'
}));
};
export const functionsController = {
execute,
state
execute,
state
};
export const Functions = {
Phone,
Phone,
Code
Code
};

View File

@@ -1,325 +1,325 @@
<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;
const { state } = functionsController;
type Method = {
icon: string;
label: string;
};
type Method = {
icon: string;
label: string;
};
$: methods = [
$state.submit === 'success' && {
icon: '/images/animations/stripe.png',
label: 'Stripe'
},
{
icon: '/images/animations/credit-card.svg',
label: 'Card'
},
{
icon: '/images/animations/paypal.svg',
label: 'PayPal'
},
{
icon: '/images/animations/apple.svg',
label: 'Apple'
}
].filter(Boolean) as Method[];
$: methods = [
$state.submit === 'success' && {
icon: '/images/animations/stripe.png',
label: 'Stripe'
},
{
icon: '/images/animations/credit-card.svg',
label: 'Card'
},
{
icon: '/images/animations/paypal.svg',
label: 'PayPal'
},
{
icon: '/images/animations/apple.svg',
label: 'Apple'
}
].filter(Boolean) as Method[];
</script>
<div data-theme-ignore class="inner-phone theme-light">
<div class="header">
<p class="title">Upgrade plan</p>
<span class="icon-menu" aria-label="menu" />
</div>
<div data-theme-ignore class="inner-phone light">
<div class="header">
<p class="title">Upgrade plan</p>
<span class="icon-menu" aria-label="menu" />
</div>
<div class="plan">
<p class="title">Premium plan</p>
<div class="subscription">
<p class="price">$20</p>
<p class="period">/month</p>
</div>
<ul>
<li>Premium plan</li>
<li>Premium plan</li>
<li>Premium plan</li>
</ul>
</div>
<div class="plan">
<p class="title">Premium plan</p>
<div class="subscription">
<p class="price">$20</p>
<p class="period">/month</p>
</div>
<ul>
<li>Premium plan</li>
<li>Premium plan</li>
<li>Premium plan</li>
</ul>
</div>
<ul class="methods">
{#each methods as method, i (method.label)}
<li
in:scale={{ delay: 150 }}
animate:flip={{ duration: 500 }}
data-active={i == 0 ? '' : undefined}
>
<img src={method.icon} alt="" />
<p>{method.label}</p>
</li>
{/each}
</ul>
<ul class="methods">
{#each methods as method, i (method.label)}
<li
in:scale={{ delay: 150 }}
animate:flip={{ duration: 500 }}
data-active={i == 0 ? '' : undefined}
>
<img src={method.icon} alt="" />
<p>{method.label}</p>
</li>
{/each}
</ul>
{#if $state.submit !== 'success'}
<div class="form">
<p>Card information</p>
<div class="bordered">
<div>
<p>placeholder</p>
<img src="/images/animations/visa.png" alt="" />
<img src="/images/animations/mastercard.png" alt="" />
</div>
<div>
<p>MM/YY</p>
<p>CVV</p>
</div>
</div>
</div>
{/if}
{#if $state.submit !== 'success'}
<div class="form">
<p>Card information</p>
<div class="bordered">
<div>
<p>placeholder</p>
<img src="/images/animations/visa.png" alt="" />
<img src="/images/animations/mastercard.png" alt="" />
</div>
<div>
<p>MM/YY</p>
<p>CVV</p>
</div>
</div>
</div>
{/if}
<button>
Pay $20.00
{#if $state.submit === 'success'}
<span in:slide={{ axis: 'x' }}>on Stripe</span>
{/if}
</button>
<button>
Pay $20.00
{#if $state.submit === 'success'}
<span in:slide={{ axis: 'x' }}>on Stripe</span>
{/if}
</button>
</div>
<style lang="scss">
.inner-phone {
padding-block: 3rem 1.5rem;
padding-inline: 1rem;
.inner-phone {
padding-block: 3rem 1.5rem;
padding-inline: 1rem;
color: rgba(67, 67, 71, 1);
text-align: left;
color: rgba(67, 67, 71, 1);
text-align: left;
position: relative;
height: 100%;
overflow: visible;
position: relative;
height: 100%;
overflow: visible;
display: flex;
flex-direction: column;
display: flex;
flex-direction: column;
.header {
display: flex;
justify-content: space-between;
align-items: center;
.header {
display: flex;
justify-content: space-between;
align-items: center;
.title {
color: #434347;
font-family: Inter;
font-size: 1rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
.title {
color: #434347;
font-family: Inter;
font-size: 1rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
}
.plan {
display: flex;
padding: 0.75rem 1rem;
flex-direction: column;
justify-content: center;
align-items: flex-start;
.plan {
display: flex;
padding: 0.75rem 1rem;
flex-direction: column;
justify-content: center;
align-items: flex-start;
border-radius: 0.75rem;
background: rgba(237, 237, 240, 0.5);
border-radius: 0.75rem;
background: rgba(237, 237, 240, 0.5);
margin-block-start: 1rem;
margin-block-start: 1rem;
.title {
color: var(--color-greyscale-700, #56565c);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 700;
line-height: 1.25rem; /* 166.667% */
}
.title {
color: var(--color-greyscale-700, #56565c);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 700;
line-height: 1.25rem; /* 166.667% */
}
.subscription {
display: flex;
align-items: baseline;
margin-block-start: 0.15rem;
.subscription {
display: flex;
align-items: baseline;
margin-block-start: 0.15rem;
.price {
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 1.125rem;
font-style: normal;
font-weight: 700;
line-height: 1.25rem; /* 111.111% */
}
.price {
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 1.125rem;
font-style: normal;
font-weight: 700;
line-height: 1.25rem; /* 111.111% */
}
.period {
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem;
}
}
.period {
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem;
}
}
ul {
display: flex;
flex-direction: column;
gap: 0.125rem;
margin-block-start: 0.75rem;
ul {
display: flex;
flex-direction: column;
gap: 0.125rem;
margin-block-start: 0.75rem;
li {
color: var(--color-greyscale-500, #818186);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 166.667% */
li {
color: var(--color-greyscale-500, #818186);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 166.667% */
padding-inline-start: 1.5rem;
padding-inline-start: 1.5rem;
position: relative;
position: relative;
&::before {
content: '';
display: block;
position: absolute;
left: 0;
width: 1rem;
height: 1rem;
top: 50%;
transform: translateY(-50%);
&::before {
content: '';
display: block;
position: absolute;
left: 0;
width: 1rem;
height: 1rem;
top: 50%;
transform: translateY(-50%);
background-image: url('/images/animations/check-circle.svg');
}
}
}
}
background-image: url('/images/animations/check-circle.svg');
}
}
}
}
.methods {
margin-block-start: 1.25rem;
.methods {
margin-block-start: 1.25rem;
display: flex;
gap: 0.75rem;
overflow: hidden;
margin-inline: -1rem;
padding-inline: 1rem;
display: flex;
gap: 0.75rem;
overflow: hidden;
margin-inline: -1rem;
padding-inline: 1rem;
li {
flex-shrink: 0;
li {
flex-shrink: 0;
display: flex;
width: 5.5rem;
padding: 0.75rem 0.75rem 0.625rem 0.75rem;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 0.125rem;
display: flex;
width: 5.5rem;
padding: 0.75rem 0.75rem 0.625rem 0.75rem;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 0.125rem;
border-radius: 0.75rem;
border: 1px solid var(--greyscale-50, #ededf0);
border-radius: 0.75rem;
border: 1px solid var(--greyscale-50, #ededf0);
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 166.667% */
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 1.25rem; /* 166.667% */
&[data-active] {
border-color: var(--appwrite-purple, #7c67fe);
}
}
}
&[data-active] {
border-color: var(--appwrite-purple, #7c67fe);
}
}
}
.form {
margin-block-start: 1.25rem;
.form {
margin-block-start: 1.25rem;
> p {
color: var(--dark-neutrals-150, #373b4d);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.125rem */
}
> p {
color: var(--dark-neutrals-150, #373b4d);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.125rem */
}
.bordered {
margin-block-start: 0.25rem;
.bordered {
margin-block-start: 0.25rem;
border-radius: 0.5rem;
border: 1px solid #ededf0;
background: var(--color-bw-white, #fff);
border-radius: 0.5rem;
border: 1px solid #ededf0;
background: var(--color-bw-white, #fff);
> div:first-child {
display: flex;
gap: 0.25rem;
padding: 0.65rem 0.75rem;
border-bottom: 1px solid #ededf0;
> div:first-child {
display: flex;
gap: 0.25rem;
padding: 0.65rem 0.75rem;
border-bottom: 1px solid #ededf0;
> :nth-child(2) {
margin-inline-start: auto;
}
}
> :nth-child(2) {
margin-inline-start: auto;
}
}
> div:nth-child(2) {
display: flex;
> div:nth-child(2) {
display: flex;
> p {
padding: 0.65rem 0.75rem;
}
> p {
padding: 0.65rem 0.75rem;
}
> p:first-child {
flex-grow: 3;
}
> p:first-child {
flex-grow: 3;
}
> p:last-child {
flex-grow: 1;
border-inline-start: 1px solid #ededf0;
}
}
> p:last-child {
flex-grow: 1;
border-inline-start: 1px solid #ededf0;
}
}
p {
color: var(--light-neutrals-50, #c4c6d7);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.125rem */
}
}
}
p {
color: var(--light-neutrals-50, #c4c6d7);
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.125rem */
}
}
}
> button {
display: flex;
text-align: center;
padding: 0.5rem 1rem;
justify-content: center;
align-items: center;
gap: 0.25rem;
> button {
display: flex;
text-align: center;
padding: 0.5rem 1rem;
justify-content: center;
align-items: center;
gap: 0.25rem;
border-radius: 0.5rem;
background: var(--appwrite-purple, #7c67fe);
box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.06);
border-radius: 0.5rem;
background: var(--appwrite-purple, #7c67fe);
box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.06);
color: var(--color-bw-white, #fff);
text-align: center;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.01225rem;
color: var(--color-bw-white, #fff);
text-align: center;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.01225rem;
margin-block-start: auto;
margin-block-start: auto;
white-space: nowrap;
}
}
white-space: nowrap;
}
}
</style>

View File

@@ -19,7 +19,7 @@
</div>
{/if}
<div data-theme-ignore class="inner-phone theme-light">
<div data-theme-ignore class="inner-phone light">
<div class="header">
<p class="title">Your tasks</p>
<span class="icon-menu" aria-label="menu" />

View File

@@ -12,42 +12,42 @@ 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() }
];
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() }
];
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
]);
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
]);
boxesAndStates.forEach(({ box, state }, i) => {
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 });
});
boxesAndStates.forEach(({ box, state }, i) => {
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 });
});
};
export const postController = {
execute,
state: {
requests,
databases,
authentication,
storage,
bandwidth,
executions,
realtime
}
execute,
state: {
requests,
databases,
authentication,
storage,
bandwidth,
executions,
realtime
}
};

View File

@@ -1,297 +1,300 @@
<script lang="ts">
import { toScale } from '$lib/utils/toScale';
import { postController } from '.';
import { elId } from '../Products.svelte';
import { toScale } from '$lib/utils/toScale';
import { postController } from '.';
import { elId } from '../Products.svelte';
const {
state: { authentication, bandwidth, databases, executions, requests, storage, realtime }
} = postController;
const {
state: { authentication, bandwidth, databases, executions, requests, storage, realtime }
} = postController;
const formatK = (num: number) => {
if (num > 999) {
return `${(num / 1000).toFixed(1)}K`;
}
return Math.floor(num);
};
const formatK = (num: number) => {
if (num > 999) {
return `${(num / 1000).toFixed(1)}K`;
}
return Math.floor(num);
};
</script>
<div class="gradient-box auth" id="post-auth-{$elId}">
<div class="u-flex u-cross-center u-gap-8">
<p class="icon-user-group" />
<p class="f-eyebrow">Authentication</p>
</div>
<p class="f-display mbs-16">{formatK(toScale($authentication, [0, 1], [0, 4000]))}</p>
<div class="u-flex u-cross-center justify-between mbs-4">
<p class="f-sub">Users</p>
<p class="f-idk">Sessions: 20K</p>
</div>
<div class="flex u-cross-center gap-2">
<p class="icon-user-group" />
<p class="f-eyebrow">Authentication</p>
</div>
<p class="f-display mbs-16">{formatK(toScale($authentication, [0, 1], [0, 4000]))}</p>
<div class="flex u-cross-center justify-between mbs-4">
<p class="f-sub">Users</p>
<p class="f-idk">Sessions: 20K</p>
</div>
</div>
<div class="gradient-box storage" id="post-storage-{$elId}">
<div class="u-flex u-cross-center u-gap-8">
<p class="icon-folder" />
<p class="f-eyebrow">Storage</p>
</div>
<p class="f-display mbs-16">
{toScale($storage, [0, 1], [0, 8]).toFixed(1)} <span class="f-tiny-display">GB</span>
</p>
<div class="u-flex u-cross-center justify-between mbs-4">
<p class="f-sub">Storage</p>
<p class="f-idk">Buckets: 44</p>
</div>
<div class="flex u-cross-center gap-2">
<p class="icon-folder" />
<p class="f-eyebrow">Storage</p>
</div>
<p class="f-display mbs-16">
{toScale($storage, [0, 1], [0, 8]).toFixed(1)} <span class="f-tiny-display">GB</span>
</p>
<div class="flex u-cross-center justify-between mbs-4">
<p class="f-sub">Storage</p>
<p class="f-idk">Buckets: 44</p>
</div>
</div>
<div class="gradient-box bandwidth" id="post-bandwidth-{$elId}">
<p class="f-display">
{toScale($bandwidth, [0, 1], [0, 1.2]).toFixed(2)} <span class="f-tiny-display">GB</span>
</p>
<p class="f-sub">Bandwidth</p>
<img class="mbs-16" src="./images/animations/bandwidth-graph.svg" alt="" />
<p class="f-display">
{toScale($bandwidth, [0, 1], [0, 1.2]).toFixed(2)} <span class="f-tiny-display">GB</span>
</p>
<p class="f-sub">Bandwidth</p>
<img class="mbs-16" src="./images/animations/bandwidth-graph.svg" alt="" />
</div>
<div class="gradient-box functions" id="post-functions-{$elId}">
<div class="u-flex u-cross-center u-gap-8">
<p class="icon-lightning-bolt" />
<p class="f-eyebrow">Functions</p>
</div>
<p class="f-display mbs-16">{toScale($executions, [0, 1], [0, 846]).toFixed(0)}</p>
<div class="u-flex u-cross-center justify-between mbs-4">
<p class="f-sub">Executions</p>
</div>
<div class="flex u-cross-center gap-2">
<p class="icon-lightning-bolt" />
<p class="f-eyebrow">Functions</p>
</div>
<p class="f-display mbs-16">{toScale($executions, [0, 1], [0, 846]).toFixed(0)}</p>
<div class="flex u-cross-center justify-between mbs-4">
<p class="f-sub">Executions</p>
</div>
</div>
<div class="gradient-box databases" id="post-databases-{$elId}">
<div class="u-flex u-cross-center u-gap-8">
<p class="icon-database" />
<p class="f-eyebrow">Databases</p>
</div>
<p class="f-display mbs-16">{toScale($databases, [0, 1], [0, 8]).toFixed(0)}</p>
<div class="u-flex u-cross-center justify-between mbs-4">
<p class="f-sub">Databases</p>
<p class="f-idk">Documents: 20</p>
</div>
<div class="flex u-cross-center gap-2">
<p class="icon-database" />
<p class="f-eyebrow">Databases</p>
</div>
<p class="f-display mbs-16">{toScale($databases, [0, 1], [0, 8]).toFixed(0)}</p>
<div class="flex u-cross-center justify-between mbs-4">
<p class="f-sub">Databases</p>
<p class="f-idk">Documents: 20</p>
</div>
</div>
<div class="gradient-box requests" id="post-requests-{$elId}">
<p class="f-display">{formatK(toScale($requests, [0, 1], [0, 6849]))}</p>
<p class="f-sub">Requests</p>
<img class="mbs-16" src="./images/animations/requests-graph.svg" alt="" />
<p class="f-display">{formatK(toScale($requests, [0, 1], [0, 6849]))}</p>
<p class="f-sub">Requests</p>
<img class="mbs-16" src="./images/animations/requests-graph.svg" alt="" />
</div>
<div class="gradient-box realtime" id="post-realtime-{$elId}">
<p class="f-display">{formatK(toScale($realtime, [0, 1], [0, 100000]))}</p>
<p class="f-sub">Realtime connections</p>
<img class="mbs-16" src="./images/animations/realtime-graph.svg" alt="" />
<p class="f-display">{formatK(toScale($realtime, [0, 1], [0, 100000]))}</p>
<p class="f-sub">Realtime connections</p>
<img class="mbs-16" src="./images/animations/realtime-graph.svg" alt="" />
</div>
<div class="gradient-overlay u-flex u-flex-vertical">
<h3>See your products grow</h3>
<p>
Keep track of your projects progress on the Appwrite Console and see them grow into products
users love and use every day.
</p>
<div class="gradient-overlay flex u-flex-vertical">
<h3>See your products grow</h3>
<p>
Keep track of your projects progress on the Appwrite Console and see them grow into products
users love and use every day.
</p>
</div>
<style lang="scss">
@use '$scss/abstract/mixins/border-gradient' as gradients;
// Utilities
.f-eyebrow {
color: #adadb0;
/* Eyebrow headings/level 3 */
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 500;
line-height: 150%; /* 1.125rem */
letter-spacing: 0.09rem;
text-transform: uppercase;
}
// Utilities
.f-eyebrow {
color: #adadb0;
/* Eyebrow headings/level 3 */
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 500;
line-height: 150%; /* 1.125rem */
letter-spacing: 0.09rem;
text-transform: uppercase;
}
.f-display {
color: #ededf0;
font-family: Aeonik Pro;
font-size: 2rem;
font-style: normal;
font-weight: 500;
line-height: 2.25rem; /* 112.5% */
}
.f-display {
color: #ededf0;
font-family: Aeonik Pro;
font-size: 2rem;
font-style: normal;
font-weight: 500;
line-height: 2.25rem; /* 112.5% */
}
.f-tiny-display {
color: var(--greyscale-50, #ededf0);
text-align: center;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1rem; /* 114.286% */
}
.f-tiny-display {
color: var(--greyscale-50, #ededf0);
text-align: center;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1rem; /* 114.286% */
}
.f-sub {
color: #97979b;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.125rem; /* 128.571% */
}
.f-sub {
color: #97979b;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.125rem; /* 128.571% */
}
.f-idk {
color: var(--primary, #e4e4e7);
text-align: right;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.3125rem */
opacity: 40%;
}
.f-idk {
color: var(--primary, #e4e4e7);
text-align: right;
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 150%; /* 1.3125rem */
opacity: 40%;
}
.mbs-16 {
margin-block-start: 1rem;
}
.mbs-16 {
margin-block-start: 1rem;
}
.mbs-4 {
margin-block-start: 0.25rem;
}
.mbs-4 {
margin-block-start: 0.25rem;
}
.justify-between {
justify-content: space-between;
}
.justify-between {
justify-content: space-between;
}
// Components
.gradient-box {
@include gradients.border-gradient;
--m-border-gradient-before: linear-gradient(
180deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(255, 255, 255, 0) 125.11%
);
--m-border-radius: 1rem;
// Components
.gradient-box {
@include gradients.border-gradient;
--m-border-gradient-before: linear-gradient(
180deg,
rgba(255, 255, 255, 0.12) 0%,
rgba(255, 255, 255, 0) 125.11%
);
--m-border-radius: 1rem;
position: absolute;
background: var(--card, rgba(35, 35, 37, 0.9));
box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0.06), -2px 4px 9px 0px rgba(0, 0, 0, 0.06),
-8px 15px 17px 0px rgba(0, 0, 0, 0.05), -19px 34px 23px 0px rgba(0, 0, 0, 0.03),
-33px 60px 27px 0px rgba(0, 0, 0, 0.01), -52px 94px 30px 0px rgba(0, 0, 0, 0);
backdrop-filter: blur(8px);
position: absolute;
background: var(--card, rgba(35, 35, 37, 0.9));
box-shadow:
0px 0px 0px 0px rgba(0, 0, 0, 0.06),
-2px 4px 9px 0px rgba(0, 0, 0, 0.06),
-8px 15px 17px 0px rgba(0, 0, 0, 0.05),
-19px 34px 23px 0px rgba(0, 0, 0, 0.03),
-33px 60px 27px 0px rgba(0, 0, 0, 0.01),
-52px 94px 30px 0px rgba(0, 0, 0, 0);
backdrop-filter: blur(8px);
padding: 1.5rem;
padding: 1.5rem;
z-index: 9999;
z-index: 9999;
min-width: 20rem;
}
min-width: 20rem;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.gradient-overlay {
position: absolute;
z-index: 100;
bottom: -7.5rem;
width: 100%;
height: 30rem;
.gradient-overlay {
position: absolute;
z-index: 100;
bottom: -7.5rem;
width: 100%;
height: 30rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 1.25rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 1.25rem;
padding-block-start: 15rem;
padding-block-start: 15rem;
opacity: 0;
animation: fadeIn 0.75s ease-in-out 1.5s forwards;
opacity: 0;
animation: fadeIn 0.75s ease-in-out 1.5s forwards;
&::before {
content: '';
inset: 0;
position: absolute;
//background: #19191d; // old bg
//filter: blur(125px); // break Safari
background: #19191dcc;
filter: blur(67px);
&::before {
content: '';
inset: 0;
position: absolute;
//background: #19191d; // old bg
//filter: blur(125px); // break Safari
background: #19191dcc;
filter: blur(67px);
}
}
h3 {
position: relative;
color: var(--primary, #e4e4e7);
text-align: center;
h3 {
position: relative;
color: var(--primary, #e4e4e7);
text-align: center;
/* Desktop/Display */
font-family: Aeonik Pro;
font-size: 4rem;
font-style: normal;
font-weight: 400;
line-height: 4.25rem; /* 106.25% */
letter-spacing: -0.04rem;
}
/* Desktop/Display */
font-family: Aeonik Pro;
font-size: 4rem;
font-style: normal;
font-weight: 400;
line-height: 4.25rem; /* 106.25% */
letter-spacing: -0.04rem;
}
p {
position: relative;
p {
position: relative;
color: var(--secondary, #adadb0);
text-align: center;
color: var(--secondary, #adadb0);
text-align: center;
/* Desktop/Description */
font-family: Inter;
font-size: 1.25rem;
font-style: normal;
font-weight: 500;
line-height: 1.75rem; /* 140% */
letter-spacing: -0.0175rem;
max-width: 40rem;
text-align: center;
}
}
/* Desktop/Description */
font-family: Inter;
font-size: 1.25rem;
font-style: normal;
font-weight: 500;
line-height: 1.75rem; /* 140% */
letter-spacing: -0.0175rem;
max-width: 40rem;
text-align: center;
}
}
// Specifics
.auth {
opacity: 0;
left: 4rem;
top: -11rem;
}
// Specifics
.auth {
opacity: 0;
left: 4rem;
top: -11rem;
}
.storage {
opacity: 0;
left: -10rem;
top: -2rem;
}
.storage {
opacity: 0;
left: -10rem;
top: -2rem;
}
.bandwidth {
opacity: 0;
left: -4rem;
top: 11rem;
}
.bandwidth {
opacity: 0;
left: -4rem;
top: 11rem;
}
.functions {
opacity: 0;
left: -6rem;
top: 35rem;
}
.functions {
opacity: 0;
left: -6rem;
top: 35rem;
}
.databases {
opacity: 0;
top: -13rem;
right: 10rem;
}
.databases {
opacity: 0;
top: -13rem;
right: 10rem;
}
.requests {
opacity: 0;
top: 17rem;
right: -18rem;
}
.requests {
opacity: 0;
top: 17rem;
right: -18rem;
}
.realtime {
opacity: 0;
top: -1rem;
right: -7rem;
}
.realtime {
opacity: 0;
top: -1rem;
right: -7rem;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,39 @@
<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;
const { state } = storageController;
</script>
<div class="pseudo-table">
<div class="header">
<span class="web-eyebrow">Filename</span>
<span class="web-eyebrow">Type</span>
<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="img-wrapper">
<img src={file.src} alt="" />
<span>{file.filename}</span>
</div>
<span class="truncated">{file.type}</span>
<span class="truncated">{file.size}</span>
</div>
{/each}
<div class="header">
<span class="web-eyebrow">Filename</span>
<span class="web-eyebrow">Type</span>
<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="img-wrapper">
<img src={file.src} alt="" />
<span>{file.filename}</span>
</div>
<span class="truncated">{file.type}</span>
<span class="truncated">{file.size}</span>
</div>
{/each}
</div>
<style lang="scss">
.header,
.row {
grid-template-columns: 7rem 1fr 1fr !important;
gap: 1.5rem 3rem;
}
.header,
.row {
grid-template-columns: 7rem 1fr 1fr !important;
gap: 1.5rem 3rem;
}
.img-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
.img-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
</style>

View File

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

View File

@@ -7,130 +7,131 @@ import { createResettable } from '$lib/utils/resettable';
import { getElSelector } from '../Products.svelte';
type File = {
src: string;
filename: string;
type: string;
size: string;
src: string;
filename: string;
type: string;
size: string;
};
type State = {
files: File[];
files: File[];
};
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();
const { update } = state.reset();
await Promise.all([
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
]);
await Promise.all([
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
]);
await safeAnimate(code, { zIndex: 20 }, { duration: 0 })?.finished;
await safeAnimate(code, { zIndex: 20 }, { duration: 0 })?.finished;
update((p) => ({
...p,
files: [
...p.files,
{
src: '/images/animations/storage-1.png',
filename: 'Profile.png',
type: 'image/png',
size: '362.6 KB'
}
]
}));
update((p) => ({
...p,
files: [
...p.files,
{
src: '/images/animations/storage-1.png',
filename: 'Profile.png',
type: 'image/png',
size: '362.6 KB'
}
]
}));
await sleep(250);
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
]);
await Promise.all([
safeAnimate(overlay, { opacity: 1 }, { duration: 0.25 })?.finished,
safeAnimate(drawer, { y: [128, 0], opacity: 1 }, { duration: 0.5 })?.finished
]);
await sleep(250);
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 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
]);
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
]);
await sleep(250);
await sleep(250);
await safeAnimate(box, { x: 300, y: 300 }, { duration: 0 })?.finished;
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 })?.finished,
safeAnimate(box, { y: [300 - 16, 300], opacity: 1 }, { duration: 1 })?.finished
]);
await Promise.all([
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
]);
await sleep(250);
await sleep(250);
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
]);
await sleep(250);
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
]);
await sleep(250);
await safeAnimate(upload, { opacity: 0, y: 48 }, { duration: 0.5 })?.finished;
await safeAnimate(upload, { opacity: 0, y: 48 }, { duration: 0.5 })?.finished;
update((p) => ({
...p,
files: [
...p.files,
{
src: '/images/animations/storage-2.png',
filename: 'Vector.svg',
type: 'vector/svg',
size: '1.5 KB'
}
]
}));
update((p) => ({
...p,
files: [
...p.files,
{
src: '/images/animations/storage-2.png',
filename: 'Vector.svg',
type: 'vector/svg',
size: '1.5 KB'
}
]
}));
await sleep(250);
await sleep(250);
update((p) => ({
...p,
files: [
...p.files,
{
src: '/images/animations/storage-3.png',
filename: 'img2.webp',
type: 'image/webp',
size: '3.2 MB'
}
]
}));
update((p) => ({
...p,
files: [
...p.files,
{
src: '/images/animations/storage-3.png',
filename: 'img2.webp',
type: 'image/webp',
size: '3.2 MB'
}
]
}));
};
export const storageController = {
execute,
state
execute,
state
};
export const Storage = {
Phone,
Box,
Code
Phone,
Box,
Code
};

View File

@@ -1,284 +1,284 @@
<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;
const { state: dbState } = databasesController;
const fixedTasks = $dbState.tasks;
const { state } = storageController;
const { state } = storageController;
</script>
<div data-theme-ignore class="inner-phone theme-light">
<div class="header">
<p class="title">Your tasks</p>
<span class="icon-menu" aria-label="menu" />
</div>
<div data-theme-ignore class="inner-phone light">
<div class="header">
<p class="title">Your tasks</p>
<span class="icon-menu" aria-label="menu" />
</div>
<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 }}>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
{/each}
</div>
<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 }}>
<TaskCheckbox bind:checked={task.checked} />
<span class="title">{task.title}</span>
</div>
{/each}
</div>
<button class="add-btn">
<span class="web-icon-plus" />
</button>
<button class="add-btn">
<span class="web-icon-plus" />
</button>
<div class="overlay" id="overlay-{$elId}">
<div class="drawer" id="drawer-{$elId}">
<p class="title">Edit images for website</p>
<p class="subtitle">Edit the attached images to use in the website</p>
<div class="overlay" id="overlay-{$elId}">
<div class="drawer" id="drawer-{$elId}">
<p class="title">Edit images for website</p>
<p class="subtitle">Edit the attached images to use in the website</p>
<div class="upload" id="upload-btn-{$elId}">Upload media...</div>
<div class="images">
{#each $state.files.slice(1) as file}
<img src={file.src} alt="" transition:fly={{ x: 16 }} />
{/each}
</div>
</div>
</div>
<div class="upload" id="upload-btn-{$elId}">Upload media...</div>
<div class="images">
{#each $state.files.slice(1) as file}
<img src={file.src} alt="" transition:fly={{ x: 16 }} />
{/each}
</div>
</div>
</div>
</div>
<div class="upload-media" id="upload-{$elId}">
<p class="title">Upload media</p>
<div class="drop-zone">
<span id="upload-text-{$elId}"> Drop media here </span>
<div class="loading-overlay" id="upload-loading-{$elId}">
<div class="loader" />
</div>
</div>
<img id="upload-img-{$elId}" src="/images/animations/storage-2.png" alt="" />
<p class="title">Upload media</p>
<div class="drop-zone">
<span id="upload-text-{$elId}"> Drop media here </span>
<div class="loading-overlay" id="upload-loading-{$elId}">
<div class="loader" />
</div>
</div>
<img id="upload-img-{$elId}" src="/images/animations/storage-2.png" alt="" />
</div>
<style lang="scss">
.inner-phone {
padding-block: 3rem;
padding-inline: 1rem;
.inner-phone {
padding-block: 3rem;
padding-inline: 1rem;
color: rgba(67, 67, 71, 1);
text-align: left;
color: rgba(67, 67, 71, 1);
text-align: left;
position: relative;
height: 100%;
overflow: visible;
position: relative;
height: 100%;
overflow: visible;
.header {
display: flex;
justify-content: space-between;
align-items: center;
.header {
display: flex;
justify-content: space-between;
align-items: center;
.title {
color: var(--color-greyscale-800, #2d2d31);
font-family: Inter;
font-size: 1rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
.title {
color: var(--color-greyscale-800, #2d2d31);
font-family: Inter;
font-size: 1rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
}
[class*='icon-'] {
font-size: 1.25rem;
color: hsl(var(--web-color-greyscale-500));
}
}
.date {
margin-block-start: 3rem;
.date {
margin-block-start: 3rem;
color: hsl(var(--web-color-greyscale-600));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 500;
line-height: 1.25rem; /* 166.667% */
}
color: hsl(var(--web-color-greyscale-600));
font-family: Inter;
font-size: 0.75rem;
font-style: normal;
font-weight: 500;
line-height: 1.25rem; /* 166.667% */
}
.tasks {
margin-block-start: 0.5rem;
.tasks {
margin-block-start: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
.task {
display: flex;
align-items: center;
gap: 0.75rem;
.task {
display: flex;
align-items: center;
gap: 0.75rem;
border-radius: 0.5rem;
border: 1px solid hsl(var(--web-color-greyscale-50));
background: hsl(var(--web-color-white));
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
border-radius: 0.5rem;
border: 1px solid hsl(var(--web-color-greyscale-50));
background: hsl(var(--web-color-white));
color: var(--greyscale-700, var(--color-greyscale-700, #56565c));
padding-block: 0.55rem;
padding-inline: 0.88rem;
padding-block: 0.55rem;
padding-inline: 0.88rem;
/* Responsive/SubBody-400 */
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.00394rem;
/* Responsive/SubBody-400 */
font-family: Inter;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.375rem; /* 157.143% */
letter-spacing: -0.00394rem;
transition: opacity 200ms ease;
transition: opacity 200ms ease;
&[data-checked] {
opacity: 0.6;
}
}
}
&[data-checked] {
opacity: 0.6;
}
}
}
.add-btn {
position: absolute;
right: 1rem;
bottom: 2.5rem;
.add-btn {
position: absolute;
right: 1rem;
bottom: 2.5rem;
width: 2.5rem;
height: 2.5rem;
flex-shrink: 0;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.12);
background-color: rgba(124, 103, 254, 1);
color: rgba(237, 237, 240, 1);
font-size: 1.5rem;
width: 2.5rem;
height: 2.5rem;
flex-shrink: 0;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.12);
background-color: rgba(124, 103, 254, 1);
color: rgba(237, 237, 240, 1);
font-size: 1.5rem;
display: grid;
place-items: center;
border-radius: 100%;
}
}
display: grid;
place-items: center;
border-radius: 100%;
}
}
.overlay {
opacity: 0;
.overlay {
opacity: 0;
position: absolute;
inset: 0;
overflow: hidden;
position: absolute;
inset: 0;
overflow: hidden;
background: rgba(0, 0, 0, 0.32);
border-radius: 2rem;
background: rgba(0, 0, 0, 0.32);
border-radius: 2rem;
.drawer {
position: absolute;
bottom: 0;
height: 60%;
opacity: 0;
.drawer {
position: absolute;
bottom: 0;
height: 60%;
opacity: 0;
background-color: white;
border-radius: 0.88463rem 0.88463rem 2rem 2rem;
background-color: white;
border-radius: 0.88463rem 0.88463rem 2rem 2rem;
padding: 1rem;
padding: 1rem;
.title {
color: #434347;
font-family: Inter;
font-size: 0.88463rem;
font-style: normal;
font-weight: 600;
line-height: 1.21638rem; /* 137.5% */
letter-spacing: -0.01238rem;
}
.title {
color: #434347;
font-family: Inter;
font-size: 0.88463rem;
font-style: normal;
font-weight: 600;
line-height: 1.21638rem; /* 137.5% */
letter-spacing: -0.01238rem;
}
.subtitle {
color: var(--greyscale-500, var(--color-greyscale-500, #818186));
font-family: Inter;
font-size: 0.77406rem;
font-style: normal;
font-weight: 400;
line-height: 1.10575rem; /* 142.857% */
letter-spacing: -0.01081rem;
.subtitle {
color: var(--greyscale-500, var(--color-greyscale-500, #818186));
font-family: Inter;
font-size: 0.77406rem;
font-style: normal;
font-weight: 400;
line-height: 1.10575rem; /* 142.857% */
letter-spacing: -0.01081rem;
margin-block-start: 0.2rem;
}
margin-block-start: 0.2rem;
}
.upload {
display: flex;
padding: 0.44231rem 0.66344rem;
justify-content: center;
align-items: center;
align-self: stretch;
.upload {
display: flex;
padding: 0.44231rem 0.66344rem;
justify-content: center;
align-items: center;
align-self: stretch;
border-radius: 0.66344rem;
border: 1px dashed #d9d9d9;
border-radius: 0.66344rem;
border: 1px dashed #d9d9d9;
color: var(--greyscale-500, var(--color-greyscale-500, #818186));
font-family: Inter;
font-size: 0.77406rem;
font-style: normal;
font-weight: 400;
line-height: 1.10575rem; /* 142.857% */
letter-spacing: -0.01081rem;
color: var(--greyscale-500, var(--color-greyscale-500, #818186));
font-family: Inter;
font-size: 0.77406rem;
font-style: normal;
font-weight: 400;
line-height: 1.10575rem; /* 142.857% */
letter-spacing: -0.01081rem;
margin-block-start: 2rem;
}
}
}
margin-block-start: 2rem;
}
}
}
.upload-media {
position: absolute;
right: calc(0% - 24px);
bottom: 8rem;
background-color: white;
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
border-radius: 1rem;
padding: 0.75rem;
.upload-media {
position: absolute;
right: calc(0% - 24px);
bottom: 8rem;
background-color: white;
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
border-radius: 1rem;
padding: 0.75rem;
opacity: 0;
opacity: 0;
.title {
color: var(--color-greyscale-800, #2d2d31);
font-family: Inter;
font-size: 0.85rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
.title {
color: var(--color-greyscale-800, #2d2d31);
font-family: Inter;
font-size: 0.85rem;
font-style: normal;
font-weight: 600;
line-height: 1.375rem; /* 137.5% */
letter-spacing: -0.014rem;
}
.drop-zone {
display: grid;
place-items: center;
border: 0.885px dashed #d9d9d9;
border-radius: 0.5rem;
.drop-zone {
display: grid;
place-items: center;
border: 0.885px dashed #d9d9d9;
border-radius: 0.5rem;
color: var(--greyscale-500, var(--color-greyscale-500, #818186));
padding: 2rem 1.25rem;
margin-block-start: 0.5rem;
font-size: 0.65rem;
font-family: Inter;
color: var(--greyscale-500, var(--color-greyscale-500, #818186));
padding: 2rem 1.25rem;
margin-block-start: 0.5rem;
font-size: 0.65rem;
font-family: Inter;
position: relative;
overflow: hidden;
position: relative;
overflow: hidden;
.loading-overlay {
position: absolute;
inset: 0;
opacity: 0;
z-index: 100;
.loading-overlay {
position: absolute;
inset: 0;
opacity: 0;
z-index: 100;
display: grid;
place-items: center;
}
}
display: grid;
place-items: center;
}
}
img {
position: absolute;
left: 0;
top: 0;
opacity: 0;
}
}
img {
position: absolute;
left: 0;
top: 0;
opacity: 0;
}
}
.images {
display: flex;
margin-block-start: 0.5rem;
gap: 0.5rem;
}
.images {
display: flex;
margin-block-start: 0.5rem;
gap: 0.5rem;
}
</style>

View File

@@ -25,34 +25,21 @@
</script>
<div
class="scroll-indicator"
class="scroll-indicator relative h-full w-px shrink-0 rounded-full"
use:rect={elRect}
style:--y={`${y}px`}
style:--percentage={`${easedPercentage * 100}%`}
>
<div class="web-dot" />
<div class="0)] absolute -top-[8px] left-1/2 translate-[-50%_var(--y," />
</div>
<style lang="scss">
.scroll-indicator {
position: relative;
width: 1px;
flex-shrink: 0;
height: 100%;
background: linear-gradient(
to bottom,
hsl(var(--web-color-accent)) 0%,
hsl(var(--web-color-greyscale-700)) var(--percentage),
hsl(var(--web-color-greyscale-700)) 100%
);
border-radius: 100%;
}
.web-dot {
position: absolute;
left: 50%;
translate: -50% var(--y, 0);
top: -8px;
}
</style>

View File

@@ -2,9 +2,7 @@ import { PUBLIC_APPWRITE_PROJECT_INIT_ID } from '$env/static/public';
import { Client, Account } from '@appwrite.io/console';
const client = new Client();
client
.setEndpoint('https://cloud.appwrite.io/v1')
.setProject(PUBLIC_APPWRITE_PROJECT_INIT_ID);
client.setEndpoint('https://cloud.appwrite.io/v1').setProject(PUBLIC_APPWRITE_PROJECT_INIT_ID);
export const appwriteInit = {
client,

View File

@@ -7,4 +7,4 @@
padding-block-start: 0;
padding-block-end: 2rem;
}
</style>
</style>

View File

@@ -1,7 +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
}
export { Root as Accordion, Item as AccordionItem };

View File

@@ -27,8 +27,14 @@
{title}
</h4>
<div class="web-author">
<div class="u-flex u-cross-center u-gap-8">
<img class="web-author-image" src={avatar} width="24" height="24" alt={author} />
<div class="flex u-cross-center gap-2">
<img
class="web-author-image"
src={avatar}
width="24"
height="24"
alt={author}
/>
<div class="web-author-info">
<h4 class="web-sub-body-400 web-u-color-text-primary">{author}</h4>
<ul class="web-metadata web-caption-400 web-is-not-mobile">

View File

@@ -42,9 +42,9 @@
</script>
<div>
<div class="u-flex u-flex-wrap u-cross-center u-margin-block-start-8">
<div class="flex flex-wrap u-cross-center u-margin-block-start-8">
<slot name="header" />
<div class="u-flex u-gap-12 u-cross-end u-margin-inline-start-auto">
<div class="flex gap-3 u-cross-end u-margin-inline-start-auto">
<button
class="web-icon-button"
aria-label="Move carousel backward"
@@ -108,7 +108,11 @@
&::after {
right: 0;
background: linear-gradient(to left, hsl(var(--web-color-background-docs)), transparent);
background: linear-gradient(
to left,
hsl(var(--web-color-background-docs)),
transparent
);
}
&[data-state='end']::after {

View File

@@ -49,12 +49,12 @@
<section class="web-content-footer">
<header class="web-content-footer-header u-width-full-line">
<div
class="u-flex u-gap-32 u-main-space-between u-cross-center u-width-full-line"
class="flex gap-8 u-main-space-between u-cross-center u-width-full-line"
style="flex-wrap: wrap-reverse;"
>
<div class="u-flex u-gap-16 u-cross-center">
<div class="flex gap-4 u-cross-center">
<h5 class="web-main-body-600 web-u-color-text-primary">Was this page helpful?</h5>
<div class="u-flex u-gap-8">
<div class="flex gap-2">
<button
class="web-radio-button"
aria-label="helpful"
@@ -88,7 +88,7 @@
href="https://github.com/appwrite/website"
target="_blank"
rel="noopener noreferrer"
class="web-link u-flex u-gap-4 u-cross-baseline"
class="web-link flex gap-1 u-cross-baseline"
>
<span class="icon-pencil-alt u-contents" aria-hidden="true" />
<span>Update on GitHub</span>
@@ -104,7 +104,7 @@
class="web-card is-normal"
style="--card-padding:1rem"
>
<div class="u-flex-vertical u-gap-8">
<div class="u-flex-vertical gap-2">
<label for="message">
<span class="web-u-color-text-primary">
What did you {feedbackType === 'negative' ? 'dislike' : 'like'}? (optional)
@@ -139,7 +139,7 @@
</p>
{/if}
<div class="u-flex u-main-end u-margin-block-start-16 u-gap-8">
<div class="flex u-main-end u-margin-block-start-16 gap-2">
<button class="web-button is-text" on:click={() => (showFeedback = false)}>
<span>Cancel</span>
</button>

View File

@@ -23,7 +23,7 @@
export let images: Array<string>;
</script>
<div class="u-position-absolute web-u-hide-mobile root">
<div class="absolute web-u-hide-mobile root">
{#each headPositions as [size, top, left], i}
{@const image = clamp(0, images.length - 1, i % images.length)}
<FloatingHead
@@ -33,7 +33,7 @@
{size}
/>
<div style:margin-block-end="0" style:padding="10%">
<img style:border-radius="50%" class="u-block" alt="" />
<img style:border-radius="50%" class="block" alt="" />
</div>
{/each}
</div>

View File

@@ -37,7 +37,7 @@
{ label: 'Functions', href: '/docs/products/functions' },
{ label: 'Messaging', href: '/products/messaging' },
{ label: 'Storage', href: '/docs/products/storage' },
{ label: 'Realtime', href: '/docs/apis/realtime' },
{ label: 'Realtime', href: '/docs/apis/realtime' }
],
Learn: [
{ label: 'Docs', href: '/docs' },
@@ -92,7 +92,7 @@
<nav
aria-label="Footer"
class="web-footer-nav u-margin-block-start-100 u-position-relative"
class="web-footer-nav u-margin-block-start-100 relative"
class:web-u-sep-block-start={!noBorder}
>
<img class="web-logo" src="/images/logos/appwrite.svg" alt="appwrite" height="24" width="130" />

View File

@@ -4,12 +4,12 @@
export let variant: 'homepage' | 'docs' = 'homepage';
const year = new Date().getFullYear()
const year = new Date().getFullYear();
</script>
{#if variant === 'homepage'}
<footer class="web-main-footer u-position-relative u-margin-block-start-48">
<ul class="u-flex u-gap-8">
<footer class="web-main-footer relative u-margin-block-start-48">
<ul class="flex gap-2">
{#each socials as social}
<li>
<a
@@ -24,8 +24,8 @@
</li>
{/each}
</ul>
<div class="u-flex u-gap-16">
<ul class="u-flex u-gap-8">
<div class="flex gap-4">
<ul class="flex gap-2">
<li><a class="web-link" href="/terms">Terms</a></li>
<li><a class="web-link" href="/privacy">Privacy</a></li>
<li><a class="web-link" href="/cookies">Cookies</a></li>
@@ -34,11 +34,9 @@
</div>
</footer>
{:else if variant === 'docs'}
<footer
class="web-main-footer is-with-bg-color u-margin-block-start-32 u-small u-position-relative"
>
<footer class="web-main-footer is-with-bg-color u-margin-block-start-32 u-small relative">
<div class="web-main-footer-grid-1">
<ul class="web-main-footer-grid-1-column-1 u-flex u-gap-8">
<ul class="web-main-footer-grid-1-column-1 flex gap-2">
{#each socials as social}
<li>
<a
@@ -77,7 +75,7 @@
{/if}
<style lang="scss">
.web-icon-button {
display: grid;
}
</style>
.web-icon-button {
display: grid;
}
</style>

View File

@@ -16,8 +16,11 @@
<nav class="web-side-nav web-is-not-desktop" class:u-hide={!open}>
<div class="web-side-nav-wrapper web-u-padding-inline-16">
<div class="u-flex items-center u-gap-8">
<a href="https://cloud.appwrite.io/register" class="web-button is-secondary web-u-flex-1">
<div class="flex items-center gap-2">
<a
href="https://cloud.appwrite.io/register"
class="web-button is-secondary web-u-flex-1"
>
Sign up
</a>
<IsLoggedIn classes="web-u-flex-1" />

View File

@@ -1,181 +1,194 @@
<script context="module" lang="ts">
export async function newsletter(name: string, email: string) {
const response = await fetch('https://growth.appwrite.io/v1/newsletter/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
email
})
});
return response;
}
export async function newsletter(name: string, email: string) {
const response = await fetch('https://growth.appwrite.io/v1/newsletter/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
email
})
});
return response;
}
</script>
<script lang="ts">
let email = '';
let name = '';
let submitted = false;
let error: string | undefined;
let submitting = false;
let email = '';
let name = '';
let submitted = false;
let error: string | undefined;
let submitting = false;
async function submit() {
submitting = true;
error = undefined;
const response = await newsletter(name, email);
submitting = false;
if (response.status >= 400) {
error = response.status >= 500 ? 'Server Error.' : 'Error submitting form.';
return;
}
submitted = true;
}
async function submit() {
submitting = true;
error = undefined;
const response = await newsletter(name, email);
submitting = false;
if (response.status >= 400) {
error = response.status >= 500 ? 'Server Error.' : 'Error submitting form.';
return;
}
submitted = true;
}
</script>
<div class="pre-footer-bg" style="pointer-events:none;">
<svg
xmlns="http://www.w3.org/2000/svg"
width="692"
height="1171"
viewBox="0 0 692 1171"
fill="none"
style="max-inline-size:100%;"
>
<g opacity="0.4" filter="url(#filter0_f_1577_37321)">
<path
d="M-96.9811 29.2126C-329.155 33.7322 -513.706 225.611 -509.186 457.785C-504.667 689.959 -312.788 874.51 -80.6141 869.99C33.1857 867.775 -132.237 523.592 -36.8339 437.579C62.4044 348.109 394.063 627.529 391.759 509.155C387.239 276.98 135.193 24.693 -96.9811 29.2126Z"
fill="url(#paint0_radial_1577_37321)"
/>
</g>
<defs>
<filter
id="filter0_f_1577_37321"
x="-809.268"
y="-270.847"
width="1501.04"
height="1440.92"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_1577_37321" />
</filter>
<radialGradient
id="paint0_radial_1577_37321"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-88.7975 449.601) rotate(178.885) scale(420.468 420.468)"
>
<stop offset="0.281696" stop-color="#FE9567" />
<stop offset="0.59375" stop-color="#FD366E" />
</radialGradient>
</defs>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="692"
height="1171"
viewBox="0 0 692 1171"
fill="none"
style="max-inline-size:100%;"
>
<g opacity="0.4" filter="url(#filter0_f_1577_37321)">
<path
d="M-96.9811 29.2126C-329.155 33.7322 -513.706 225.611 -509.186 457.785C-504.667 689.959 -312.788 874.51 -80.6141 869.99C33.1857 867.775 -132.237 523.592 -36.8339 437.579C62.4044 348.109 394.063 627.529 391.759 509.155C387.239 276.98 135.193 24.693 -96.9811 29.2126Z"
fill="url(#paint0_radial_1577_37321)"
/>
</g>
<defs>
<filter
id="filter0_f_1577_37321"
x="-809.268"
y="-270.847"
width="1501.04"
height="1440.92"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_1577_37321" />
</filter>
<radialGradient
id="paint0_radial_1577_37321"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-88.7975 449.601) rotate(178.885) scale(420.468 420.468)"
>
<stop offset="0.281696" stop-color="#FE9567" />
<stop offset="0.59375" stop-color="#FD366E" />
</radialGradient>
</defs>
</svg>
</div>
<div class="web-big-padding-section">
<div class="web-big-padding-section-level-1">
<div class="web-big-padding-section-level-2">
<div class="web-container">
<div class="web-grid-1-1-opt-2 u-gap-32">
<div class="">
<div class="web-u-max-inline-size-none-mobile" class:web-u-max-width-380={!submitted}>
<section class="u-flex-vertical web-u-gap-20">
<h1 class="web-title web-u-color-text-primary">Subscribe to our newsletter</h1>
<p class="web-description web-u-padding-block-end-40">
Sign up to our company blog and get the latest insights from Appwrite. Learn more
about engineering, product design, building community, and tips & tricks for using
Appwrite.
</p>
</section>
</div>
</div>
{#if submitted}
<div class="u-flex u-gap-8 u-cross-center">
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="9"
cy="9"
r="8"
fill="#FD366E"
fill-opacity="0.08"
stroke="#FD366E"
stroke-opacity="0.32"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5.25 10.5L7.75 12.5L12.75 6"
stroke="#E4E4E7"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<div class="web-big-padding-section-level-1">
<div class="web-big-padding-section-level-2">
<div class="web-container">
<div class="grid grid-cols-2-opt-2 gap-8">
<div class="">
<div
class="web-u-max-inline-size-none-mobile"
class:web-u-max-width-380={!submitted}
>
<section class="u-flex-vertical web-gap-5">
<h1 class="web-title web-u-color-text-primary">
Subscribe to our newsletter
</h1>
<p class="web-description web-u-padding-block-end-40">
Sign up to our company blog and get the latest insights from
Appwrite. Learn more about engineering, product design, building
community, and tips & tricks for using Appwrite.
</p>
</section>
</div>
</div>
{#if submitted}
<div class="flex gap-2 u-cross-center">
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="9"
cy="9"
r="8"
fill="#FD366E"
fill-opacity="0.08"
stroke="#FD366E"
stroke-opacity="0.32"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5.25 10.5L7.75 12.5L12.75 6"
stroke="#E4E4E7"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<span class="text">
Thank you for subscribing! An email has been sent to your inbox.
</span>
</div>
{:else}
<form method="post" on:submit|preventDefault={submit} class="u-flex-vertical u-gap-16">
<div class="u-flex u-flex-vertical u-gap-4">
<label for="name">Your name</label>
<input
class="web-input-text"
type="text"
placeholder="Enter your name"
id="name"
name="name"
required
bind:value={name}
/>
</div>
<div class="u-flex u-flex-vertical u-gap-4">
<label for="email">Your email</label>
<input
class="web-input-text"
type="email"
placeholder="Enter your email"
required
id="email"
name="email"
bind:value={email}
/>
</div>
<button type="submit" class="web-button" disabled={submitting}>Sign up</button>
{#if error}
<span class="text"> Something went wrong. Please try again later. </span>
{/if}
</form>
{/if}
</div>
</div>
</div>
</div>
<span class="text">
Thank you for subscribing! An email has been sent to your inbox.
</span>
</div>
{:else}
<form
method="post"
on:submit|preventDefault={submit}
class="u-flex-vertical gap-4"
>
<div class="flex u-flex-vertical gap-1">
<label for="name">Your name</label>
<input
class="web-input-text"
type="text"
placeholder="Enter your name"
id="name"
name="name"
required
bind:value={name}
/>
</div>
<div class="flex u-flex-vertical gap-1">
<label for="email">Your email</label>
<input
class="web-input-text"
type="email"
placeholder="Enter your email"
required
id="email"
name="email"
bind:value={email}
/>
</div>
<button type="submit" class="web-button" disabled={submitting}
>Sign up</button
>
{#if error}
<span class="text">
Something went wrong. Please try again later.
</span>
{/if}
</form>
{/if}
</div>
</div>
</div>
</div>
</div>
<style lang="scss">
.pre-footer-bg {
position: absolute;
top: clamp(300px, 50vw, 50%);
left: clamp(300px, 50vw, 50%);
transform: translate(-50%, -70%);
width: clamp(1200px, 100vw, 3000px);
height: auto;
max-inline-size: unset;
max-block-size: unset;
}
.pre-footer-bg {
position: absolute;
top: clamp(300px, 50vw, 50%);
left: clamp(300px, 50vw, 50%);
transform: translate(-50%, -70%);
width: clamp(1200px, 100vw, 3000px);
height: auto;
max-inline-size: unset;
max-block-size: unset;
}
</style>

View File

@@ -3,9 +3,9 @@
<img src="/images/bgs/pre-footer.png" alt="" class="web-pre-footer-bg" style="z-index:-1" />
<div class="web-grid-1-1 u-gap-32 web-u-row-gap-80 u-position-relative">
<section class="web-hero u-flex web-u-row-gap-32 u-main-center u-cross-center">
<h2 class="web-display u-max-width-500 web-u-text-align-center web-u-color-text-primary">
<div class="grid grid-cols-2 gap-8 web-u-row-gap-80 relative">
<section class="web-hero flex web-u-row-gap-32 u-main-center u-cross-center">
<h2 class="web-display u-max-width-500 text-center web-u-color-text-primary">
Start building today
</h2>
<a
@@ -16,7 +16,7 @@
</a>
</section>
<section
class="web-card is-transparent has-border-gradient web-u-max-inline-width-584-mobile web-u-margin-inline-auto-mobile web-u-inline-width-100-percent-mobile"
class="web-card is-transparent has-border-gradient web-u-max-inline-width-584-mobile web-mx-auto-mobile web-u-inline-width-100-percent-mobile"
>
<header class="web-strip-plans-header">
<div class="web-strip-plans-header-wrapper web-u-row-gap-24">

View File

@@ -61,7 +61,7 @@
}
function createHref(hit: Hit<Props>): string {
const anchor = hit.anchor === '#' ? '' : hit.anchor ?? '';
const anchor = hit.anchor === '#' ? '' : (hit.anchor ?? '');
const target = hit.url + anchor;
return target.toString();
@@ -147,7 +147,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="wrapper u-position-fixed u-padding-0 u-inset-0 u-flex u-main-center u-cross-center"
class="wrapper u-padding-0 u-inset-0 u-main-center u-cross-center fixed flex"
data-visible={open ? true : undefined}
style:z-index="100"
style:background="hsl(var(--web-color-black) / 0.3)"
@@ -159,15 +159,11 @@
<div
class="web-input-text-search-wrapper web-u-max-width-680 web-u-margin-inline-20 u-width-full-line"
>
<span
class="web-icon-search u-z-index-5"
aria-hidden="true"
style="inset-block-start:0.9rem"
/>
<span class="web-icon-search z-[5]" aria-hidden="true" style="inset-block-start:0.9rem" />
<div id="searchbox" />
<input
class="web-input-button -u-padding-block-0 u-position-relative u-z-index-1"
class="web-input-button -u-padding-block-0 relative z-1"
type="text"
id="search"
bind:value
@@ -184,7 +180,7 @@
}}
/>
<div
class="web-card is-normal u-flex-vertical u-gap-24"
class="web-card is-normal u-flex-vertical gap-6"
use:melt={$menu}
style="--card-padding-mobile:1rem; border-radius:0 0 0.5rem 0.5rem;"
>
@@ -192,15 +188,15 @@
<section>
{#if results.length > 0}
<h6 class="web-eyebrow">{results.length} results found</h6>
<ul class="u-flex-vertical u-gap-4 u-margin-block-start-8">
<ul class="u-flex-vertical u-margin-block-start-8 gap-1">
{#each results as hit, i (hit.uid)}
{@const relevantSubtitle = getRelevantSubtitle(hit)}
<li>
<a
data-hit={i}
href={createHref(hit)}
class="web-button web-caption-400 is-text u-flex-vertical u-gap-8 u-min-width-100-percent
web-u-padding-block-8 web-padding-inline-12 web-u-cross-start u-max-width-100-percent"
class="web-button web-caption-400 is-text u-flex-vertical u-min-width-100-percent web-u-padding-block-8
web-padding-inline-12 web-u-cross-start u-max-width-100-percent gap-2"
use:melt={$option({
value: hit,
label: hit.title ?? i.toString()
@@ -216,9 +212,7 @@
{/if}
</div>
{#if hit.p}
<div
class="web-u-color-text-secondary web-u-trim-1"
>
<div class="web-u-color-text-secondary web-u-trim-1">
{hit.p}
</div>
{/if}
@@ -235,7 +229,7 @@
{/if}
<section>
<h6 class="web-eyebrow">Recommended</h6>
<ul class="u-flex-vertical u-gap-4 u-margin-block-start-8">
<ul class="u-flex-vertical u-margin-block-start-8 gap-1">
{#each recommended as hit, i (hit.uid)}
{@const index = i + (results.length ? results.length : 0)}
<li>
@@ -246,7 +240,7 @@
value: hit,
label: hit.title ?? i.toString()
})}
class="web-button web-caption-400 is-text u-flex-vertical u-gap-8 u-min-width-100-percent web-u-padding-block-4 web-u-cross-start"
class="web-button web-caption-400 is-text u-flex-vertical u-min-width-100-percent web-u-padding-block-4 web-u-cross-start gap-2"
>
<div class="web-u-trim-1">
<span class="web-u-color-text-secondary">{hit.h1}</span>

View File

@@ -106,7 +106,7 @@
{#each groups as group}
{@const isDefault = group.label === DEFAULT_GROUP}
{#if isDefault}
<div class="u-flex u-flex-vertical u-gap-2">
<div class="flex u-flex-vertical gap-0.5">
{#each group.options as option}
<button class="web-select-option" use:melt={$optionEl(option)}>
{#if option.icon}

View File

@@ -1,68 +1,68 @@
<script lang="ts">
import { createSwitch, melt } from '@melt-ui/svelte';
import { createSwitch, melt } from '@melt-ui/svelte';
export let checked = false;
export let checked = false;
const {
elements: { root },
states: { checked: meltChecked }
} = createSwitch({
onCheckedChange({ next }) {
checked = next;
return next;
}
});
const {
elements: { root },
states: { checked: meltChecked }
} = createSwitch({
onCheckedChange({ next }) {
checked = next;
return next;
}
});
$: meltChecked.set(checked);
$: meltChecked.set(checked);
</script>
<div class="melt-switch">
<button use:melt={$root}>
<span class="thumb" />
</button>
<button use:melt={$root}>
<span class="thumb" />
</button>
</div>
<style lang="scss">
.melt-switch {
display: flex;
align-items: center;
}
.melt-switch {
display: flex;
align-items: center;
}
button {
--padding: 0.125rem;
--width: 2.25rem;
button {
--padding: 0.125rem;
--width: 2.25rem;
position: relative;
height: 1.5rem;
width: var(--width);
cursor: default;
border-radius: 9999px;
background-color: #19191d;
transition: ease 150ms;
}
position: relative;
height: 1.5rem;
width: var(--width);
cursor: default;
border-radius: 9999px;
background-color: #19191d;
transition: ease 150ms;
}
.melt-switch :global([data-state='checked']) {
background-color: #fd366e;
}
.melt-switch :global([data-state='checked']) {
background-color: #fd366e;
}
.thumb {
--thumb-size: 1.25rem;
.thumb {
--thumb-size: 1.25rem;
position: absolute;
top: 50%;
position: absolute;
top: 50%;
display: block;
height: var(--thumb-size);
width: var(--thumb-size);
border-radius: 9999px;
background-color: #ffffff;
transform: translateX(var(--padding)) translateY(-50%);
display: block;
height: var(--thumb-size);
width: var(--thumb-size);
border-radius: 9999px;
background-color: #ffffff;
transform: translateX(var(--padding)) translateY(-50%);
transition: ease 150ms;
}
transition: ease 150ms;
}
:global(button[data-state='checked']) .thumb {
--x: calc(var(--width) - var(--thumb-size) - var(--padding));
transform: translateX(var(--x)) translateY(-50%);
}
:global(button[data-state='checked']) .thumb {
--x: calc(var(--width) - var(--thumb-size) - var(--padding));
transform: translateX(var(--x)) translateY(-50%);
}
</style>

View File

@@ -38,7 +38,7 @@
href: '/docs/quick-starts/angular',
image: `/images/platforms/${$themeInUse}/angular.svg`
},
{
{
name: 'Refine',
href: '/docs/quick-starts/refine',
image: `/images/platforms/${$themeInUse}/refine.svg`
@@ -57,8 +57,7 @@
name: 'React Native',
href: '/docs/quick-starts/react-native',
image: `/images/platforms/${$themeInUse}/react-native.svg`
},
}
] as Array<{
name: string;
href: string;
@@ -66,12 +65,17 @@
}>;
</script>
<ul class="u-flex u-flex-wrap u-gap-16 web-u-margin-block-32-mobile web-u-margin-block-40-not-mobile">
<ul class="flex flex-wrap gap-4 web-u-margin-block-32-mobile web-u-margin-block-40-not-mobile">
{#each platforms as platform}
<Tooltip>
<li>
<a href={platform.href} class="web-icon-button web-box-icon has-border-gradient">
<img src={platform.image} alt="{platform.name} quick start" width="32" height="32" />
<img
src={platform.image}
alt="{platform.name} quick start"
width="32"
height="32"
/>
</a>
</li>
<svelte:fragment slot="tooltip">{platform.name}</svelte:fragment>

View File

@@ -16,7 +16,10 @@
<aside class="web-grid-120-1fr-auto-side" class:web-is-mobile-closed={!showToc}>
<div class="web-page-steps">
<div class="web-page-steps-location web-is-not-mobile" style="--location:{progress * 100}%;">
<div
class="web-page-steps-location web-is-not-mobile"
style="--location:{progress * 100}%;"
>
<span class="web-page-steps-location-button">
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@@ -1,6 +1,7 @@
export const GITHUB_STARS = '41.9K';
export const BANNER_KEY = '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 SENTRY_DSN =
'https://27d41dc8bb67b596f137924ab8599e59@o1063647.ingest.us.sentry.io/4507497727000576';
/**
* History:

View File

@@ -78,7 +78,7 @@
<svelte:window on:keydown={handleKeydown} />
<div class="u-position-relative">
<div class="relative">
<section class="web-mobile-header is-transparent">
<div class="web-mobile-header-start">
<a href="/" aria-label="homepage">
@@ -102,7 +102,11 @@
<a href="https://cloud.appwrite.io" class="web-button web-is-only-desktop">
<span class="web-sub-body-500">Go to Console</span>
</a>
<button class="web-button is-text" aria-label="open navigation" on:click={toggleSidenav}>
<button
class="web-button is-text"
aria-label="open navigation"
on:click={toggleSidenav}
>
{#if $layoutState.showSidenav}
<span aria-hidden="true" class="web-icon-close" />
{:else}
@@ -140,7 +144,7 @@
</li>
</ul>
</nav>
<div class="u-flex u-stretch web-u-margin-inline-start-48">
<div class="flex u-stretch web-u-margin-inline-start-48">
<button
class="web-input-button web-u-flex-basis-400"
on:click={() => ($layoutState.showSearch = true)}
@@ -148,7 +152,7 @@
<span class="web-icon-search" aria-hidden="true" />
<span class="text">Search in docs</span>
<div class="u-flex u-gap-4 u-margin-inline-start-auto">
<div class="flex gap-1 u-margin-inline-start-auto">
{#if isMac()}
<span class="web-kbd" aria-label="command"></span>
{:else}
@@ -160,7 +164,7 @@
</div>
</div>
<div class="web-main-header-end">
<div class="u-flex u-gap-8">
<div class="flex gap-2">
<a
href="https://github.com/appwrite/appwrite/stargazers"
target="_blank"

View File

@@ -39,13 +39,13 @@
<ul class="web-metadata web-caption-400">
<slot name="metadata" />
</ul>
<div class="u-position-relative u-flex u-cross-center">
<div class="relative flex u-cross-center">
{#if back}
<a
href={back}
class="
web-button is-text is-icon web-u-cross-center web-u-size-40
u-position-absolute u-inset-inline-start-0 web-u-translate-x-negative"
absolute u-inset-inline-start-0 web-u-translate-x-negative"
aria-label="previous page"
>
<span
@@ -66,14 +66,12 @@
<aside class="web-references-menu web-u-padding-inline-start-24">
<div class="web-references-menu-content">
{#if toc && toc.length > 0}
<div class="u-flex u-main-space-between u-cross-center u-gap-16">
<div class="flex u-main-space-between u-cross-center gap-4">
<h5 class="web-references-menu-title web-eyebrow">On This Page</h5>
</div>
<ol class="web-references-menu-list">
{#each toc as parent (parent.href)}
<li
class="web-references-menu-item"
>
<li class="web-references-menu-item">
<a
href={parent.href}
class="web-references-menu-link"
@@ -94,7 +92,8 @@
href={child.href}
class="web-references-menu-link"
>
<span class="web-caption-400">{child.title}</span
<span class="web-caption-400"
>{child.title}</span
>
</a>
</li>
@@ -105,10 +104,7 @@
{/each}
</ol>
<div class="u-sep-block-start u-padding-block-start-20">
<button
class="web-link u-inline-flex u-cross-center u-gap-8"
use:scrollToTop
>
<button class="web-link u-inline-flex u-cross-center gap-2" use:scrollToTop>
<span class="web-icon-arrow-up" aria-hidden="true" />
<span class="web-caption-400">Back to top</span>
</button>
@@ -117,4 +113,4 @@
</div>
</aside>
</article>
</main>
</main>

View File

@@ -20,22 +20,19 @@
<article class="web-article u-contents">
<header class="web-article-header">
<div class="web-article-header-start u-flex-vertical web-u-cross-start">
<button
class="web-icon-button web-is-only-mobile"
aria-label="previous page"
>
<button class="web-icon-button web-is-only-mobile" aria-label="previous page">
<span class="icon-cheveron-left" aria-hidden="true" />
</button>
<ul class="web-metadata web-caption-400">
<slot name="metadata" />
</ul>
<div class="u-position-relative u-flex u-cross-center">
<div class="relative flex u-cross-center">
{#if back}
<a
href={back}
class="
web-button is-text is-only-icon web-u-cross-center web-u-size-40
u-position-absolute u-inset-inline-start-0 web-u-translate-x-negative"
absolute u-inset-inline-start-0 web-u-translate-x-negative"
aria-label="previous page"
>
<span
@@ -51,7 +48,7 @@
</header>
<div class="web-article-content">
<slot />
<div class="u-flex u-main-space-between">
<div class="flex u-main-space-between">
{#if prevStep}
<a href={prevStep.href} class="web-button is-text">
<span class="icon-cheveron-left" aria-hidden="true" />
@@ -82,7 +79,7 @@
</div>
<aside class="web-references-menu web-u-padding-inline-start-24">
<div class="web-references-menu-content">
<div class="u-flex u-main-space-between u-cross-center u-gap-16">
<div class="flex u-main-space-between u-cross-center gap-4">
<h5 class="web-references-menu-title web-eyebrow">Tutorial Steps</h5>
</div>
<ol class="web-references-menu-list">
@@ -100,9 +97,9 @@
</a>
{#if isCurrentStep}
<ol
class="web-references-menu-list u-margin-block-start-16 u-margin-inline-start-32"
class="web-references-menu-list u-margin-block-start-16 u-margin-inline-start-32"
>
{#each toc as parent}
{#each toc as parent}
<li class="web-references-menu-item">
<a
href={parent.href}
@@ -136,14 +133,14 @@
</ol>
{/if}
</li>
{/each}
{/each}
</ol>
{/if}
</li>
{/each}
</ol>
<div class="u-sep-block-start u-padding-block-start-20">
<button class="web-link u-inline-flex u-cross-center u-gap-8" use:scrollToTop>
<button class="web-link u-inline-flex u-cross-center gap-2" use:scrollToTop>
<span class="web-icon-arrow-up" aria-hidden="true" />
<span class="web-caption-400">Back to top</span>
</button>
@@ -152,5 +149,3 @@
</aside>
</article>
</main>

View File

@@ -68,7 +68,7 @@
}
function getVisibleTheme() {
const themes = Array.from(document.querySelectorAll('.theme-dark, .theme-light')).filter(
const themes = Array.from(document.querySelectorAll('.theme-dark, .light')).filter(
(element) => {
const { classList, dataset } = element as HTMLElement;
if (
@@ -85,7 +85,7 @@
for (const theme of themes) {
if (isInViewport(theme)) {
return theme.classList.contains('theme-light') ? 'light' : 'dark';
return theme.classList.contains('light') ? 'light' : 'dark';
}
}
@@ -144,7 +144,7 @@
};
</script>
<div class="u-position-relative">
<div class="relative">
<section
class="web-mobile-header theme-{resolvedTheme}"
class:is-transparent={browser && !$isMobileNavOpen}

View File

@@ -59,7 +59,8 @@
<a href={parent.href} aria-label="go back">
<span class="icon-cheveron-left" aria-hidden="true" />
</a>
<span class="web-side-nav-wrapper-parent-title web-eyebrow">{parent.label}</span>
<span class="web-side-nav-wrapper-parent-title web-eyebrow">{parent.label}</span
>
</section>
{/if}
{#each navigation as navGroup}

View File

@@ -1,3 +1,3 @@
export function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
return str.charAt(0).toUpperCase() + str.slice(1);
}

View File

@@ -3,114 +3,114 @@ type Cancel = () => void;
type Promised<T> = T extends Promise<infer U> ? U : T;
type Args<T> = {
returned: Promised<T>;
cancel: Cancel;
returned: Promised<T>;
cancel: Cancel;
};
export type Chain = {
execute: () => Promise<void>;
cancel: Cancel;
execute: () => Promise<void>;
cancel: Cancel;
};
interface ChainFn {
<A>(fn1: (args: Args<undefined>) => A): Chain;
<A, B>(fn1: (args: Args<undefined>) => A, fn2: (args: Args<A>) => B): Chain;
<A, B, C>(
fn1: (args: Args<undefined>) => A,
fn2: (args: Args<A>) => B,
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
): 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
): Chain;
<A, B, C, D, E, F>(
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,
fn6: (args: Args<E>) => F
): Chain;
<A, B, C, D, E, F, G>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G
): Chain;
<A, B, C, D, E, F, G, H>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H
): Chain;
<A, B, C, D, E, F, G, H, I>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I
): Chain;
<A, B, C, D, E, F, G, H, I, J>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J
): Chain;
<A, B, C, D, E, F, G, H, I, J, K>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J,
fn11: (args: Args<J>) => K
): Chain;
<A, B, C, D, E, F, G, H, I, J, K, L>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J,
fn11: (args: Args<J>) => K,
fn12: (args: Args<K>) => L
): Chain;
// So on...
<A>(fn1: (args: Args<undefined>) => A): Chain;
<A, B>(fn1: (args: Args<undefined>) => A, fn2: (args: Args<A>) => B): Chain;
<A, B, C>(
fn1: (args: Args<undefined>) => A,
fn2: (args: Args<A>) => B,
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
): 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
): Chain;
<A, B, C, D, E, F>(
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,
fn6: (args: Args<E>) => F
): Chain;
<A, B, C, D, E, F, G>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G
): Chain;
<A, B, C, D, E, F, G, H>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H
): Chain;
<A, B, C, D, E, F, G, H, I>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I
): Chain;
<A, B, C, D, E, F, G, H, I, J>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J
): Chain;
<A, B, C, D, E, F, G, H, I, J, K>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J,
fn11: (args: Args<J>) => K
): Chain;
<A, B, C, D, E, F, G, H, I, J, K, L>(
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,
fn6: (args: Args<E>) => F,
fn7: (args: Args<F>) => G,
fn8: (args: Args<G>) => H,
fn9: (args: Args<H>) => I,
fn10: (args: Args<I>) => J,
fn11: (args: Args<J>) => K,
fn12: (args: Args<K>) => L
): Chain;
// So on...
}
/**
@@ -120,32 +120,32 @@ interface ChainFn {
* The cancel method will cancel the execution of the remaining callbacks.
*/
export const chain: ChainFn = (...fns: Function[]) => {
const cancelled = {} as Record<string, boolean>;
const cancelled = {} as Record<string, boolean>;
const cancel = () => {
Object.keys(cancelled).forEach((key) => (cancelled[key] = true));
};
const cancel = () => {
Object.keys(cancelled).forEach((key) => (cancelled[key] = true));
};
let lastRes: any = undefined;
let lastRes: any = undefined;
const execute = async () => {
const executionId = stupidId();
cancelled[executionId] = false;
const execute = async () => {
const executionId = stupidId();
cancelled[executionId] = false;
for (let i = 0; i < fns.length; i++) {
const fn = fns[i];
if (cancelled[executionId]) {
delete cancelled[executionId];
return;
}
for (let i = 0; i < fns.length; i++) {
const fn = fns[i];
if (cancelled[executionId]) {
delete cancelled[executionId];
return;
}
lastRes = await fn({ returned: lastRes, cancel });
}
lastRes = await fn({ returned: lastRes, cancel });
}
delete cancelled[executionId];
};
delete cancelled[executionId];
};
return { execute, cancel };
return { execute, cancel };
};
// Stupid way of generating unique id

View File

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

View File

@@ -1,3 +1,3 @@
export function clamp(min: number, value: number, max: number) {
return Math.min(Math.max(min, value), max);
return Math.min(Math.max(min, value), max);
}

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
export const noop = () => {
// Do nothing
// Do nothing
};

View File

@@ -1,3 +1,3 @@
export function objectKeys<T extends object>(obj: T): Array<keyof T> {
return Object.keys(obj) as Array<keyof T>;
return Object.keys(obj) as Array<keyof T>;
}

View File

@@ -1,5 +1,5 @@
export function randomPick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
return arr[Math.floor(Math.random() * arr.length)];
}
export const deterministicRandom = <T>(options: T[], seed: string): T => {

View File

@@ -2,7 +2,16 @@ import { writable } from 'svelte/store';
import type { Language } from './code';
import { browser } from '$app/environment';
const allVersions = ['1.5.x', '1.4.x', '1.3.x', '1.2.x', '1.1.x', '1.0.x', '0.15.x', 'cloud'] as const;
const allVersions = [
'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];
@@ -20,7 +29,7 @@ export enum Service {
Locale = 'locale',
Storage = 'storage',
Teams = 'teams',
Users = 'users',
Users = 'users'
}
export enum Platform {
@@ -114,7 +123,7 @@ export const serviceMap: Record<Service, string> = {
[Service.Locale]: 'Locale',
[Service.Storage]: 'Storage',
[Service.Teams]: 'Teams',
[Service.Users]: 'Users',
[Service.Users]: 'Users'
};
export const preferredVersion = writable<Version | null>(

View File

@@ -52,11 +52,11 @@ export type AppwriteSchemaObject = OpenAPIV3.SchemaObject & {
};
export interface Property {
name: string;
items?: {
type?: string;
oneOf?: OpenAPIV3.ReferenceObject[];
} & OpenAPIV3.ReferenceObject;
name: string;
items?: {
type?: string;
oneOf?: OpenAPIV3.ReferenceObject[];
} & OpenAPIV3.ReferenceObject;
}
export enum ModelType {
@@ -182,7 +182,8 @@ export function getSchema(id: string, api: OpenAPIV3.Document): OpenAPIV3.Schema
if (schema) {
return schema;
}
throw new Error(`Schema doesn't exist for id: ${id}`);}
throw new Error(`Schema doesn't exist for id: ${id}`);
}
const specs = import.meta.glob(
'$appwrite/app/config/specs/open-api3*-(client|server|console).json',
@@ -354,15 +355,18 @@ export function resolveReference(
throw new Error("Schema doesn't exist");
}
export const generateExample = (schema: OpenAPIV3.SchemaObject, api: OpenAPIV3.Document<{}>, modelType: ModelType = ModelType.REST): Object => {
const properties = Object.keys(schema.properties ?? {}).map((key) =>{
export const generateExample = (
schema: OpenAPIV3.SchemaObject,
api: OpenAPIV3.Document<{}>,
modelType: ModelType = ModelType.REST
): Object => {
const properties = Object.keys(schema.properties ?? {}).map((key) => {
const name = key;
const fields = schema.properties?.[key];
return {
name,
...fields
}
};
});
const example = properties.reduce((carry, currentValue) => {
@@ -370,29 +374,32 @@ export const generateExample = (schema: OpenAPIV3.SchemaObject, api: OpenAPIV3.D
let propertyName;
switch (modelType) {
case ModelType.REST:
propertyName = property.name;
break;
propertyName = property.name;
break;
case ModelType.GRAPHQL:
propertyName = property.name.replace('$', '_');
break;
propertyName = property.name.replace('$', '_');
break;
default:
propertyName = property.name;
break;
propertyName = property.name;
break;
}
if (property.type === 'array') {
// If it's an array type containing primatives
if (property.items?.type){
if (property.items?.type) {
return {
...carry,
[propertyName]: property['x-example']
}
};
}
if (property.items && 'anyOf' in property.items) {
// default to first child type if multiple available
const firstSchema = (property.items as unknown as AppwriteSchemaObject)?.anyOf?.[0];
const schema = getSchema(getIdFromReference(firstSchema as OpenAPIV3.ReferenceObject), api)
const schema = getSchema(
getIdFromReference(firstSchema as OpenAPIV3.ReferenceObject),
api
);
return {
...carry,
@@ -401,37 +408,43 @@ export const generateExample = (schema: OpenAPIV3.SchemaObject, api: OpenAPIV3.D
}
// if an array of objects without child types
const schema = getSchema(getIdFromReference(property.items as OpenAPIV3.ReferenceObject), api);
const schema = getSchema(
getIdFromReference(property.items as OpenAPIV3.ReferenceObject),
api
);
return {
...carry,
[propertyName]: [generateExample(schema, api, modelType)]
}
};
}
// If it's an object type, but not in an array.
if (property.type === 'object') {
if (property.items?.oneOf){
if (property.items?.oneOf) {
// default to first child type if multiple available
const schema = getSchema(getIdFromReference(property.items.oneOf[0] as OpenAPIV3.ReferenceObject), api);
const schema = getSchema(
getIdFromReference(property.items.oneOf[0] as OpenAPIV3.ReferenceObject),
api
);
return {
...carry,
[propertyName]: generateExample(schema, api, modelType)
}
};
}
if (property.items){
if (property.items) {
const schema = getSchema(getIdFromReference(property.items), api);
return {
...carry,
[propertyName]: generateExample(schema, api, modelType)
}
};
}
}
return {
...carry,
[propertyName]: property['x-example']
}
};
}, {});
return example;
}
};

View File

@@ -2,6 +2,6 @@
type UnknownFn = (...args: any[]) => any;
export type TestCases<Fn extends UnknownFn> = {
args: Parameters<Fn>;
expected: ReturnType<Fn>;
args: Parameters<Fn>;
expected: ReturnType<Fn>;
}[];

View File

@@ -4,24 +4,24 @@ import { toScale } from './toScale';
import type { TestCases } from './test';
const testCases: TestCases<typeof toScale> = [
{
args: [5, [0, 10], [0, 100]],
expected: 50
},
{
args: [0.5, [0, 1], [0, 100]],
expected: 50
},
{
args: [0.4, [0.2, 1], [0, 1]],
expected: 0.25
}
{
args: [5, [0, 10], [0, 100]],
expected: 50
},
{
args: [0.5, [0, 1], [0, 100]],
expected: 50
},
{
args: [0.4, [0.2, 1], [0, 1]],
expected: 0.25
}
];
describe('toScale', () => {
testCases.forEach(({ args, expected }) => {
it(`should return ${expected} when given ${JSON.stringify(args)}`, () => {
expect(toScale(...args)).toBe(expected);
});
});
testCases.forEach(({ args, expected }) => {
it(`should return ${expected} when given ${JSON.stringify(args)}`, () => {
expect(toScale(...args)).toBe(expected);
});
});
});

View File

@@ -8,5 +8,5 @@ export type Scale = [number, number];
* `to` { lower: 0, upper: 100 } => `number` 30
*/
export function toScale(value: number, from: Scale, to: Scale): number {
return ((value - from[0]) * (to[1] - to[0])) / (from[1] - from[0]) + to[0];
return ((value - from[0]) * (to[1] - to[0])) / (from[1] - from[0]) + to[0];
}

View File

@@ -2,21 +2,21 @@ import { base } from '$app/paths';
import type { Tutorial } from '$markdoc/layouts/Tutorial.svelte';
export function globToTutorial(data: { tutorials: Record<string, unknown>; pathname: string }) {
return Object.entries(data.tutorials)
.map(([filepath, tutorial]) => {
const { frontmatter } = tutorial as {
frontmatter: Tutorial;
};
const slug = filepath.replace('./', '').replace('/+page.markdoc', '');
const tutorialName = data.pathname.split('/').slice(0, -1).join('/');
return Object.entries(data.tutorials)
.map(([filepath, tutorial]) => {
const { frontmatter } = tutorial as {
frontmatter: Tutorial;
};
const slug = filepath.replace('./', '').replace('/+page.markdoc', '');
const tutorialName = data.pathname.split('/').slice(0, -1).join('/');
return {
title: frontmatter.title,
step: frontmatter.step,
href: `${base}${tutorialName}/${slug}`
};
})
.sort((a, b) => {
return a.step - b.step;
});
return {
title: frontmatter.title,
step: frontmatter.step,
href: `${base}${tutorialName}/${slug}`
};
})
.sort((a, b) => {
return a.step - b.step;
});
}

View File

@@ -45,10 +45,8 @@
</svelte:head>
<Main>
<div class="web-big-padding-section-level-1 u-position-relative u-overflow-hidden">
<div
class="u-position-absolute u-inset-inline-start-0 u-inset-block-end-0 web-u-pointer-events-none"
>
<div class="web-big-padding-section-level-1 relative overflow-hidden">
<div class="absolute u-inset-inline-start-0 u-inset-block-end-0 web-u-pointer-events-none">
<svg
xmlns="http://www.w3.org/2000/svg"
width="660"
@@ -102,9 +100,9 @@
</svg>
</div>
<div class="web-big-padding-section-level-2 u-position-relative">
<div class="web-big-padding-section-level-2 relative">
<div class="web-container">
<div class="web-author-section u-block">
<div class="web-author-section block">
{#if avatar}
<FloatingHead --position="relative" src={avatar} alt={name} size={112} />
{/if}
@@ -119,7 +117,7 @@
{bio}
</p>
{/if}
<ul class="u-flex u-main-center u-gap-8 u-margin-block-start-16">
<ul class="flex u-main-center gap-2 u-margin-block-start-16">
{#if github}
<li>
<a
@@ -171,7 +169,7 @@
<h2 class="web-title web-u-color-text-primary">Articles</h2>
<!-- <div class="web-is-only-mobile u-margin-block-start-32">
<label class="u-block web-select is-colored" for="articles">
<label class="block web-select is-colored" for="articles">
<select id="articles">
<option>Latest</option>
<option>News</option>
@@ -183,7 +181,7 @@
</label>
</div> -->
<!--<div class="web-is-not-mobile">
<div class="u-flex u-main-space-between u-gap-16 u-margin-block-start-24">
<div class="flex u-main-space-between gap-4 u-margin-block-start-24">
<ul
class="web-secondary-tabs is-transparent"
role="tablist"
@@ -264,7 +262,7 @@
</ul> -->
<!-- <div class="web-input-text-search-wrapper">
<span class="icon-search" aria-hidden="true" />
<input class="web-input-text web-u-block-size-48" type="search" placeholder="Search" />
<input class="web-input-text web-block-size-48" type="search" placeholder="Search" />
</div>
</div>
</div>-->
@@ -287,7 +285,7 @@
</div>
</div>
<div
class="web-big-padding-section-level-2 is-margin-replace-padding u-position-relative u-overflow-hidden"
class="web-big-padding-section-level-2 is-margin-replace-padding relative overflow-hidden"
>
<div class="web-container">
<FooterNav />

View File

@@ -53,7 +53,7 @@
</div>
<!-- <div class="web-input-text-search-wrapper u-inline-width-100-percent-mobile">
<span class="icon-search" aria-hidden="true" />
<input class="web-input-text web-u-block-size-48" type="search" placeholder="Search" />
<input class="web-input-text web-block-size-48" type="search" placeholder="Search" />
</div> -->
</div>
@@ -76,7 +76,7 @@
</ul>
</div>
<div
class="web-big-padding-section-level-2 is-margin-replace-padding u-position-relative u-overflow-hidden"
class="web-big-padding-section-level-2 is-margin-replace-padding relative overflow-hidden"
>
<div class="web-container">
<FooterNav />

View File

@@ -72,7 +72,7 @@
<h1 class="web-title web-u-color-text-primary">{title}</h1>
</header>
<button
class="toc-btn u-position-sticky u-flex u-width-full-line u-main-space-between u-cross-center
class="toc-btn sticky flex u-width-full-line u-main-space-between u-cross-center
web-u-padding-20 web-u-margin-inline-20-negative web-u-color-text-primary web-is-only-mobile
u-margin-block-start-24 web-u-sep-block web-u-filter-blur-8"
style:--inset-block-start="4.5rem"

View File

@@ -88,10 +88,7 @@
{/if}
{#if authorData}
<div class="web-author u-margin-block-start-16">
<a
href={authorData.href}
class="u-flex u-cross-center u-gap-8"
>
<a href={authorData.href} class="flex u-cross-center gap-2">
{#if authorData.avatar}
<img
class="web-author-image"
@@ -103,13 +100,15 @@
/>
{/if}
<div class="u-flex-vertical">
<h4 class="web-sub-body-400 web-u-color-text-primary">
<h4
class="web-sub-body-400 web-u-color-text-primary"
>
{authorData.name}
</h4>
<p class="web-caption-400">{authorData.role}</p>
</div>
</a>
<!-- <ul class="u-flex u-gap-8 u-margin-inline-start-auto u-cross-child-center">
<!-- <ul class="flex gap-2 u-margin-inline-start-auto u-cross-child-center">
{#if authorData.twitter}
<li>
<a
@@ -155,7 +154,7 @@
</header>
{#if cover}
<div class="web-media-container">
<Media class="u-block" src={cover} />
<Media class="block" src={cover} />
</div>
{/if}
@@ -164,7 +163,7 @@
</div>
</article>
<!-- {#if categories?.length}
<div class="u-flex u-gap-16">
<div class="flex gap-4">
{#each categories as cat}
<a href={cat.href} class="web-tag">{cat.name}</a>
{/each}
@@ -200,7 +199,7 @@
</section>
</div>
</div>
<div class="web-big-padding-section-level-2 u-position-relative u-overflow-hidden">
<div class="web-big-padding-section-level-2 relative overflow-hidden">
<div class="web-container">
<Newsletter />
<FooterNav />

View File

@@ -1,10 +1,11 @@
<script lang="ts">
import { setContext } from "svelte";
import { setContext } from 'svelte';
setContext("no-paragraph", true);
setContext('no-paragraph', true);
</script>
<blockquote class="web-blockquote">
<p class="web-description">
<slot />
</p>
<p class="web-description">
<slot />
</p>
</blockquote>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
export let content: string;
export let content: string;
</script>
<span class="web-inline-code web-code">{content}</span>

View File

@@ -65,13 +65,13 @@
<header class="web-code-snippet-header">
<div class="web-code-snippet-header-start">
{#if badgeValue}
<div class="u-flex u-gap-16">
<div class="flex gap-4">
<div class="web-tag"><span class="text">{badgeValue}</span></div>
</div>
{/if}
</div>
<div class="web-code-snippet-header-end">
<ul class="buttons-list u-flex u-gap-8">
<ul class="buttons-list flex gap-2">
<li class="buttons-list-item web-u-padding-inline-start-20">
<Tooltip>
<button

View File

@@ -1 +1 @@
<strong class="u-bold"><slot /></strong>
<strong class="u-bold"><slot /></strong>

View File

@@ -1,18 +1,18 @@
<script lang="ts">
import type { HTMLTdAttributes } from 'svelte/elements';
import type { HTMLTdAttributes } from 'svelte/elements';
export let align: HTMLTdAttributes['align'] = undefined;
export let width: HTMLTdAttributes['width'] = undefined;
export let align: HTMLTdAttributes['align'] = undefined;
export let width: HTMLTdAttributes['width'] = undefined;
</script>
<td
style:width={width ? `${width}px` : undefined}
style:min-inline-size={width ? 'unset' : undefined}
role="columnheader"
class="web-table-head-col"
{align}
style:width={width ? `${width}px` : undefined}
style:min-inline-size={width ? 'unset' : undefined}
role="columnheader"
class="web-table-head-col"
{align}
>
<span class="web-eyebrow">
<slot />
</span>
<span class="web-eyebrow">
<slot />
</span>
</td>

View File

@@ -1,3 +1,3 @@
<tr class="web-table-row">
<slot />
</tr>
<slot />
</tr>

View File

@@ -1,18 +1,18 @@
<script context="module">
export { default as Blockquote } from './Blockquote.svelte';
export { default as Blockquote } from './Blockquote.svelte';
export { default as Code } from './Code.svelte';
export { default as Fence } from './Fence.svelte';
export { default as Heading } from './Heading.svelte';
export { default as List } from './List.svelte';
export { default as Paragraph } from './Paragraph.svelte';
export { default as Strong } from './Strong.svelte';
export { default as Image } from './Image.svelte';
export { default as Item } from './Item.svelte';
export { default as Link } from './Link.svelte';
export { default as Table } from './Table.svelte';
export { default as Tbody } from './Tbody.svelte';
export { default as Td } from './Td.svelte';
export { default as Th } from './Th.svelte';
export { default as Thead } from './Thead.svelte';
export { default as Tr } from './Tr.svelte';
export { default as Heading } from './Heading.svelte';
export { default as List } from './List.svelte';
export { default as Paragraph } from './Paragraph.svelte';
export { default as Strong } from './Strong.svelte';
export { default as Image } from './Image.svelte';
export { default as Item } from './Item.svelte';
export { default as Link } from './Link.svelte';
export { default as Table } from './Table.svelte';
export { default as Tbody } from './Tbody.svelte';
export { default as Td } from './Td.svelte';
export { default as Th } from './Th.svelte';
export { default as Thead } from './Thead.svelte';
export { default as Tr } from './Tr.svelte';
</script>

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