Skip to content

Commit 631e349

Browse files
committed
Merge branch 'main' into 'revamp-designs'.
2 parents 9206e3b + 73552e5 commit 631e349

File tree

79 files changed

+1191
-624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1191
-624
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Copilot Setup Steps
2+
3+
# Automatically run the setup steps when they are changed to allow for easy validation, and
4+
# allow manual testing through the repository's "Actions" tab
5+
on:
6+
workflow_dispatch:
7+
push:
8+
paths:
9+
- .github/workflows/copilot-setup-steps.yml
10+
pull_request:
11+
paths:
12+
- .github/workflows/copilot-setup-steps.yml
13+
14+
jobs:
15+
copilot-setup-steps:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
19+
steps:
20+
- uses: actions/checkout@v5
21+
- name: Use Node.js
22+
uses: actions/setup-node@v6
23+
with:
24+
node-version: 20
25+
- name: Install pnpm
26+
uses: pnpm/action-setup@v4
27+
- name: Install dependencies
28+
run: pnpm install --frozen-lockfile

AGENTS.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Appwrite Console - Copilot Instructions
2+
3+
## Repository Overview
4+
5+
Appwrite Console is the web-based GUI for the Appwrite backend-as-a-service platform. Single-page application built with **Svelte 5 + SvelteKit 2**, **TypeScript** (not strict mode), **Vite 7**, tested with **Vitest + Playwright**. Package manager: **pnpm 10.15.1**, Node 20+. ~1500 files with extensive component-based architecture.
6+
7+
## Critical Build & Test Commands
8+
9+
### Setup (REQUIRED before any commands)
10+
11+
1. **Install pnpm**: `npm install -g corepack && corepack enable && corepack prepare pnpm@10.15.1 --activate`
12+
2. **Create .env**: `cp .env.example .env` (configure `PUBLIC_APPWRITE_ENDPOINT` and `PUBLIC_CONSOLE_MODE`)
13+
3. **Configure network access** (if using GitHub Actions or restricted environments):
14+
- Ensure firewall/proxy allows access to: `pkg.pr.new`, `pkg.vc`, `registry.npmjs.org`
15+
- These domains are required for dependencies: `@appwrite.io/console`, `@appwrite.io/pink-icons-svelte`, `@appwrite.io/pink-svelte`
16+
- In GitHub Actions: Use `pnpm/action-setup@v4` which handles registry configuration
17+
- If network errors persist, check proxy settings: `npm config get proxy` and `npm config get https-proxy`
18+
4. **Install dependencies**: `pnpm install --frozen-lockfile` (if pkg.pr.new/pkg.vc fail due to network restrictions, installation may still succeed with cached versions)
19+
20+
### Development Commands
21+
22+
**Standard workflow**: `check``lint``test``build` (before committing)
23+
24+
- `pnpm run check` - TypeScript/Svelte validation (~30-60s)
25+
- `pnpm run lint` - ESLint check (~10-20s)
26+
- `pnpm run format` - Auto-fix Prettier formatting
27+
- `pnpm run test` - Vitest unit tests with TZ=EST (~10-30s)
28+
- `pnpm run build` - Production build via build.js (~60-120s)
29+
- `pnpm dev` - Dev server on port 3000
30+
- `pnpm run preview` - Preview build on port 4173
31+
- `pnpm run e2e` - Playwright tests (needs `pnpm exec playwright install --with-deps chromium` first, ~120s+)
32+
33+
**CI Pipeline** (`.github/workflows/tests.yml`): audit → install → check → lint → test → build
34+
35+
## Project Structure
36+
37+
```
38+
src/
39+
├── lib/ # Reusable logic ($lib alias)
40+
│ ├── components/ # Feature components (billing, domains, permissions, etc.)
41+
│ ├── elements/ # Basic UI elements
42+
│ ├── helpers/ # Utility functions (array, date, string, etc.)
43+
│ ├── stores/ # Svelte stores for state
44+
│ ├── sdk/ # Appwrite SDK wrappers
45+
│ └── constants.ts, flags.ts, system.ts
46+
├── routes/
47+
│ ├── (console)/ # Auth-required routes
48+
│ │ ├── organization-[organization]/
49+
│ │ └── project-[region]-[project]/ # databases, functions, messaging, storage
50+
│ └── (public)/ # Public routes (login, register, auth callbacks)
51+
├── themes/ # Theme definitions ($themes alias)
52+
└── app.html, hooks.{client,server}.ts, service-worker.ts
53+
```
54+
55+
**SvelteKit conventions**: `+page.svelte` (component), `+page.ts` (data loader), `+layout.svelte` (wrapper), `+error.svelte` (errors). Groups like `(console)` organize routes without affecting URLs. Dynamic params: `[param]`.
56+
57+
## Key Configuration
58+
59+
**svelte.config.js**: Adapter = static SPA (fallback: index.html), base path `/console`, aliases: `$lib`, `$routes`, `$themes`
60+
**vite.config.ts**: Dev port 3000, Vitest (client=jsdom, server=node), test files: `src/**/*.{test,spec}.{js,ts}`
61+
**tsconfig.json**: Extends `.svelte-kit/tsconfig.json`, **NOT strict mode** (`strict: false`)
62+
**eslint.config.js**: Flat config (ESLint 9+), many rules disabled (see TODOs)
63+
**.prettierrc**: 4 spaces, single quotes, 100 char width, no trailing commas
64+
65+
## Testing
66+
67+
**Unit (Vitest)**: Tests in `src/lib/helpers/*.test.ts`, run with `TZ=EST` (timezone matters). Setup mocks SvelteKit (`$app/*`) in `vitest-setup-client.ts`.
68+
**E2E (Playwright)**: Tests in `e2e/journeys/*.spec.ts`, needs build+preview on port 4173, retries 3x, timeout 120s, Chromium only.
69+
70+
## Common Pitfalls
71+
72+
1. **Blank page in dev**: Disable ad blockers if seeing "Failed to fetch dynamically imported module" (known SvelteKit issue)
73+
2. **Network errors on install**:
74+
- pkg.pr.new/pkg.vc deps may fail due to firewall/proxy restrictions
75+
- Check access: `curl -I https://pkg.pr.new` and `curl -I https://pkg.vc`
76+
- Configure proxy if needed: `npm config set proxy http://proxy:port` and `npm config set https-proxy http://proxy:port`
77+
- GitHub Actions: Ensure runner has internet access; use `pnpm/action-setup@v4` action
78+
- Local dev: Often safe to continue with cached versions if network fails
79+
3. **OOM on build**: Set `NODE_OPTIONS=--max_old_space_size=8192` (like Dockerfile does)
80+
4. **Test failures**: Always use `pnpm run test` (sets TZ=EST), not `vitest` directly
81+
5. **TS errors not showing**: Run `pnpm run check` explicitly (dev server doesn't always surface them)
82+
6. **Format vs lint conflicts**: Run `pnpm run format` before `pnpm run lint`
83+
7. **E2E timeouts**: Wait 120s for preview server startup, tests auto-retry 3x
84+
8. **Stale build**: Clear `.svelte-kit` if changes not reflected: `rm -rf .svelte-kit && pnpm run build`
85+
86+
## Code Conventions
87+
88+
- Imports: Use `$lib`, `$routes`, `$themes` aliases
89+
- Components: PascalCase, in `src/lib/components/[feature]/`
90+
- Helpers: Pure functions in `src/lib/helpers/`
91+
- Types: Inline or `.d.ts`, not `.types.ts` files
92+
- Comments: Minimal, use for TODOs or complex logic
93+
- TypeScript: Not strict mode, `any` tolerated
94+
95+
## Workflow
96+
97+
1. Run Appwrite backend locally (see [docs](https://appwrite.io/docs/advanced/self-hosting))
98+
2. Configure `.env` with backend endpoint
99+
3. `pnpm install --frozen-lockfile`
100+
4. `pnpm dev` (hot reload on port 3000)
101+
5. Before commit: `pnpm run check && pnpm run format && pnpm run lint && pnpm run test && pnpm run build`
102+
6. **Take screenshots**: For any UI changes, capture screenshots and include them in the PR description or comments before finalizing
103+
104+
**Trust these instructions** - only search if incomplete/incorrect. See CONTRIBUTING.md for PR conventions. Use `--frozen-lockfile` always. Docker builds: multi-stage, final image is nginx serving static files from `/console` path.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
},
2323
"dependencies": {
2424
"@ai-sdk/svelte": "^1.1.24",
25-
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e",
25+
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752",
2626
"@appwrite.io/pink-icons": "0.25.0",
27-
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@46f65c7",
27+
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521",
2828
"@appwrite.io/pink-legacy": "^1.0.3",
29-
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@46f65c7",
29+
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521",
3030
"@faker-js/faker": "^9.9.0",
3131
"@popperjs/core": "^2.11.8",
3232
"@sentry/sveltekit": "^8.38.0",

pnpm-lock.yaml

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/commandCenter/searchers/projects.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,64 @@ import { sdk } from '$lib/stores/sdk';
44
import { Query } from '@appwrite.io/console';
55
import { get } from 'svelte/store';
66
import type { Searcher } from '../commands';
7+
import { project } from '$routes/(console)/project-[region]-[project]/store';
78
import { base } from '$app/paths';
89

910
export const projectsSearcher = (async (query: string) => {
11+
const q = query.toLowerCase().trim();
12+
const keywords = [
13+
'endpoint',
14+
'api key',
15+
'api-key',
16+
'apikey',
17+
'project id',
18+
'project-id',
19+
'api end'
20+
];
21+
22+
const wantsCredentials = keywords.some((k) => q.includes(k));
23+
24+
if (wantsCredentials) {
25+
const curr = get(project);
26+
if (curr?.$id) {
27+
return [
28+
{
29+
label: 'Go to Settings',
30+
callback: () => {
31+
goto(`${base}/project-${curr.region}-${curr.$id}/settings`);
32+
},
33+
group: 'navigation'
34+
}
35+
];
36+
}
37+
return [];
38+
}
39+
1040
const { projects } = await sdk.forConsole.projects.list({
1141
queries: [Query.equal('teamId', get(organization).$id), Query.orderDesc('')]
1242
});
1343

1444
return projects
15-
.filter((project) => project.name.toLowerCase().includes(query.toLowerCase()))
45+
.filter((project) => {
46+
const searchable = [project.name, project.$id, project.region]
47+
.filter(Boolean)
48+
.join(' ')
49+
.toLowerCase();
50+
51+
const words = q.split(/\s+/).filter(Boolean);
52+
return words.every((w) => searchable.includes(w));
53+
})
1654
.map((project) => {
55+
const href = `${base}/project-${project.region}-${project.$id}`;
56+
57+
const label = project.name;
58+
1759
return {
18-
label: project.name,
60+
label,
1961
callback: () => {
20-
goto(`${base}/project-${project.region}-${project.$id}`);
62+
goto(href);
2163
},
2264
group: 'projects'
23-
} as const;
65+
};
2466
});
2567
}) satisfies Searcher;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script lang="ts">
2+
import { Copy } from '.';
3+
import { Icon, Tag } from '@appwrite.io/pink-svelte';
4+
import { IconDuplicate } from '@appwrite.io/pink-icons-svelte';
5+
import { getProjectEndpoint } from '$lib/helpers/project';
6+
</script>
7+
8+
<Copy value={getProjectEndpoint()} copyText="Copy endpoint">
9+
<Tag size="xs" variant="code">
10+
<Icon icon={IconDuplicate} size="s" slot="start" />
11+
<span style:white-space="nowrap" style:overflow="hidden" style:word-break="break-all">
12+
API endpoint
13+
</span>
14+
</Tag>
15+
</Copy>

src/lib/components/backupRestoreBox.svelte

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,18 @@
125125
126126
onMount(() => {
127127
// fast path: don't subscribe if org is on a free plan or is self-hosted.
128-
if (isSelfHosted || (isCloud && $organization.billingPlan === BillingPlan.FREE)) return;
129-
130-
return realtime
131-
.forProject(page.params.region, page.params.project)
132-
.subscribe('console', (response) => {
133-
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
134-
135-
if (
136-
response.events.includes('archives.*') ||
137-
response.events.includes('restorations.*')
138-
) {
139-
updateOrAddItem(response.payload);
140-
}
141-
});
128+
if (isSelfHosted || (isCloud && $organization?.billingPlan === BillingPlan.FREE)) return;
129+
130+
return realtime.forProject(page.params.region, 'console', (response) => {
131+
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
132+
133+
if (
134+
response.events.includes('archives.*') ||
135+
response.events.includes('restorations.*')
136+
) {
137+
updateOrAddItem(response.payload);
138+
}
139+
});
142140
});
143141
</script>
144142

src/lib/components/copy.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
disabled={tooltipDisabled}
5858
portal={tooltipPortal}
5959
delay={tooltipDelay}
60+
maxWidth="500px"
6061
placement={tooltipPlacement}>
6162
<span
6263
data-private

src/lib/components/csvImportBox.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import { onMount } from 'svelte';
33
import { base } from '$app/paths';
44
import { page } from '$app/state';
5-
import { sdk } from '$lib/stores/sdk';
65
import { Dependencies } from '$lib/constants';
6+
import { realtime, sdk } from '$lib/stores/sdk';
77
import { goto, invalidate } from '$app/navigation';
88
import { getProjectId } from '$lib/helpers/project';
99
import { addNotification } from '$lib/stores/notifications';
@@ -187,7 +187,7 @@
187187
migrations.migrations.forEach(updateOrAddItem);
188188
});
189189
190-
return sdk.forConsoleIn(page.params.region).realtime.subscribe('console', (response) => {
190+
return realtime.forConsole(page.params.region, 'console', (response) => {
191191
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
192192
if (response.events.includes('migrations.*')) {
193193
updateOrAddItem(response.payload as Payload);

src/lib/components/id.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,11 @@
9595
export let tooltipPortal = false;
9696
export let tooltipDelay: number = 0;
9797
export let tooltipPlacement: TooltipPlacement = undefined;
98+
export let copyText: string | undefined = undefined;
9899
</script>
99100

100101
{#key value}
101-
<Copy {value} {event} {tooltipPortal} {tooltipDelay} {tooltipPlacement}>
102+
<Copy {value} {event} {tooltipPortal} {tooltipDelay} {tooltipPlacement} {copyText}>
102103
<Tag size="xs" variant="code">
103104
<Icon icon={IconDuplicate} size="s" slot="start" />
104105
<span

0 commit comments

Comments
 (0)