diff --git a/.env.example b/.env.example
index fc29a596b..5a16f982f 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,5 @@
PUBLIC_CONSOLE_MODE=self-hosted
-PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,column-encrypt,index-lengths
+PUBLIC_CONSOLE_FEATURE_FLAGS=sites,index-lengths
PUBLIC_APPWRITE_MULTI_REGION=false
PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1
PUBLIC_STRIPE_KEY=
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cfd944fa4..294dae87c 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -79,7 +79,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
"PUBLIC_CONSOLE_MODE=cloud"
- "PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,column-encrypt,index-lengths"
+ "PUBLIC_CONSOLE_FEATURE_FLAGS=sites,index-lengths"
"PUBLIC_APPWRITE_MULTI_REGION=true"
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"
@@ -118,7 +118,7 @@ jobs:
build-args: |
"PUBLIC_CONSOLE_MODE=self-hosted"
"PUBLIC_APPWRITE_MULTI_REGION=false"
- "PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,column-encrypt,index-lengths"
+ "PUBLIC_CONSOLE_FEATURE_FLAGS=sites,index-lengths"
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
publish-cloud-no-regions:
@@ -156,6 +156,6 @@ jobs:
build-args: |
"PUBLIC_CONSOLE_MODE=cloud"
"PUBLIC_APPWRITE_MULTI_REGION=false"
- "PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,column-encrypt,index-lengths"
+ "PUBLIC_CONSOLE_FEATURE_FLAGS=sites,index-lengths"
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
diff --git a/package.json b/package.json
index 71e75ba6a..5e1d8fb76 100644
--- a/package.json
+++ b/package.json
@@ -22,11 +22,11 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
- "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@4ca420f",
+ "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@56743f5",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
"@appwrite.io/pink-legacy": "^1.0.3",
- "@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d521606",
+ "@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@fee5c53",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9da094eea..6b127b486 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -24,8 +24,8 @@ importers:
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
- specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d521606
- version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d521606(svelte@5.25.3)
+ specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@fee5c53
+ version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@fee5c53(svelte@5.25.3)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -261,8 +261,8 @@ packages:
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@4ca420f}
version: 1.9.0
- '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d521606fc63e0800b996f0fbb1a8d1f6907480ed':
- resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d521606fc63e0800b996f0fbb1a8d1f6907480ed}
+ '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@fee5c539136e15c93cd3f2398d25cb217caa8dc8':
+ resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@fee5c539136e15c93cd3f2398d25cb217caa8dc8}
version: 2.0.0-RC.1
peerDependencies:
svelte: ^4.0.0
@@ -282,8 +282,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.3':
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
- '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d521606':
- resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d521606}
+ '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@fee5c53':
+ resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@fee5c53}
version: 2.0.0-RC.2
peerDependencies:
svelte: ^4.0.0
@@ -1266,6 +1266,14 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+ '@tanstack/svelte-virtual@3.13.12':
+ resolution: {integrity: sha512-SsOinCktvpMFbdSW61X6jeUNaJvD1tmt30AFVSLe1FQxGxjdPkZo0gAsoVPqM9FFZqidvvXQ4gpadDaTz/yGxA==}
+ peerDependencies:
+ svelte: ^3.48.0 || ^4.0.0 || ^5.0.0
+
+ '@tanstack/virtual-core@3.13.12':
+ resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==}
+
'@testing-library/dom@10.4.0':
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
engines: {node: '>=18'}
@@ -3638,7 +3646,7 @@ snapshots:
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@4ca420f': {}
- '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d521606fc63e0800b996f0fbb1a8d1f6907480ed(svelte@5.25.3)':
+ '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@fee5c539136e15c93cd3f2398d25cb217caa8dc8(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
@@ -3655,12 +3663,13 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
- '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d521606(svelte@5.25.3)':
+ '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@fee5c53(svelte@5.25.3)':
dependencies:
- '@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d521606fc63e0800b996f0fbb1a8d1f6907480ed(svelte@5.25.3)
+ '@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@fee5c539136e15c93cd3f2398d25cb217caa8dc8(svelte@5.25.3)
'@floating-ui/dom': 1.6.13
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3)
'@melt-ui/svelte': 0.86.6(svelte@5.25.3)
+ '@tanstack/svelte-virtual': 3.13.12(svelte@5.25.3)
ansicolor: 2.0.3
d3: 7.9.0
fuse.js: 7.1.0
@@ -4711,6 +4720,13 @@ snapshots:
dependencies:
tslib: 2.8.1
+ '@tanstack/svelte-virtual@3.13.12(svelte@5.25.3)':
+ dependencies:
+ '@tanstack/virtual-core': 3.13.12
+ svelte: 5.25.3
+
+ '@tanstack/virtual-core@3.13.12': {}
+
'@testing-library/dom@10.4.0':
dependencies:
'@babel/code-frame': 7.26.2
diff --git a/src/lib/commandCenter/panels/ai.svelte b/src/lib/commandCenter/panels/ai.svelte
index b764cfd76..5181a99a2 100644
--- a/src/lib/commandCenter/panels/ai.svelte
+++ b/src/lib/commandCenter/panels/ai.svelte
@@ -148,7 +148,12 @@
})}
clearOnCallback={false}
on:keydown={(e) => {
- if (e.detail.key !== 'Escape') {
+ const showingExamples = !$isLoading && !answer;
+ if (e.detail.key === 'Enter' && $input.trim()) {
+ e.detail.cancel();
+ return;
+ }
+ if (e.detail.key !== 'Escape' && !showingExamples) {
e.detail.cancel();
}
}}
@@ -302,15 +307,27 @@
}
}
- :global(.answer ul),
- :global(.answer ol) {
- gap: 1rem;
+ :global(.answer ul) {
+ padding-inline-start: 1rem;
+ list-style-type: disc;
display: grid;
+ gap: 1rem;
+ }
+
+ :global(.answer ol) {
+ padding-inline-start: 1.1rem;
+ list-style-type: decimal;
+ display: grid;
+ gap: 1rem;
}
:global(.answer a) {
text-decoration: underline;
}
+
+ :global(.answer a:hover) {
+ opacity: 0.8;
+ }
}
.experimental {
diff --git a/src/lib/components/billing/alerts/paymentMandate.svelte b/src/lib/components/billing/alerts/paymentMandate.svelte
index b55f5d20b..08d9207d7 100644
--- a/src/lib/components/billing/alerts/paymentMandate.svelte
+++ b/src/lib/components/billing/alerts/paymentMandate.svelte
@@ -12,7 +12,7 @@
$organization.$id,
$paymentMissingMandate.$id
);
- await confirmSetup(method.clientSecret, method.$id);
+ await confirmSetup(method.clientSecret, method.providerMethodId);
}
diff --git a/src/lib/components/billing/alerts/projectsLimit.svelte b/src/lib/components/billing/alerts/projectsLimit.svelte
new file mode 100644
index 000000000..cd3a0a3ee
--- /dev/null
+++ b/src/lib/components/billing/alerts/projectsLimit.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+{#if $currentPlan && $currentPlan.projects > 0 && !hideBillingHeaderRoutes.includes(page.url.pathname)}
+
+
+ Choose which projects to keep before {toLocaleDate(billingProjectsLimitDate)} or upgrade
+ to Pro. Projects over the limit will be blocked after this date.
+
+
+
+
+
+
+{/if}
diff --git a/src/lib/components/billing/alerts/selectProjectCloud.svelte b/src/lib/components/billing/alerts/selectProjectCloud.svelte
new file mode 100644
index 000000000..6a44161b3
--- /dev/null
+++ b/src/lib/components/billing/alerts/selectProjectCloud.svelte
@@ -0,0 +1,117 @@
+
+
+
+
+ Choose which two projects to keep. Projects over the limit will be blocked after this date.
+
+ {#if error}
+ {error}
+ {/if}
+
+
+ Project Name
+ Created
+
+ {#each projects as project}
+
+ {project.name}
+ {toLocaleDateTime(project.$createdAt)}
+
+ {/each}
+
+ {#if selectedProjects.length > $currentPlan?.projects}
+
+ You can only select {$currentPlan?.projects} projects. Please deselect others to continue.
+
+ {/if}
+ {#if selectedProjects.length === $currentPlan?.projects}
+
+
+ {@html formatProjectsToArchive()}
+ will be archived.
+
+
+ {/if}
+
+ (showSelectProject = false)}
+ >Cancel
+ Save
+
+
diff --git a/src/lib/components/bottomModalAlert.svelte b/src/lib/components/bottomModalAlert.svelte
index 406b40222..1c0f24a9f 100644
--- a/src/lib/components/bottomModalAlert.svelte
+++ b/src/lib/components/bottomModalAlert.svelte
@@ -3,7 +3,6 @@
import { hideNotification, shouldShowNotification } from '$lib/helpers/notifications';
import { app } from '$lib/stores/app';
import {
- type BottomModalAlertAction,
type BottomModalAlertItem,
bottomModalAlertsConfig,
dismissBottomModalAlert,
@@ -20,18 +19,18 @@
import { goto } from '$app/navigation';
import { Typography } from '@appwrite.io/pink-svelte';
- let currentIndex = 0;
- let openModalOnMobile = false;
+ let currentIndex = $state(0);
+ let openModalOnMobile = $state(false);
- function getPageScope(pathname: string) {
- const isProjectPage = pathname.includes('project-[region]-[project]');
- const isOrganizationPage = pathname.includes('organization-[organization]');
+ function getPageScope(route: string) {
+ const isProjectPage = route.includes('project-[region]-[project]');
+ const isOrganizationPage = route.includes('organization-[organization]');
return { isProjectPage, isOrganizationPage };
}
- function filterModalAlerts(alerts: BottomModalAlertItem[], pathname: string) {
- const { isProjectPage, isOrganizationPage } = getPageScope(pathname);
+ function filterModalAlerts(alerts: BottomModalAlertItem[], route: string) {
+ const { isProjectPage, isOrganizationPage } = getPageScope(route);
return alerts
.sort((a, b) => b.importance - a.importance)
@@ -51,19 +50,32 @@
});
}
- $: filteredModalAlerts = filterModalAlerts($bottomModalAlertsConfig.alerts, page.route.id);
+ const bottomModalAlerts = $derived($bottomModalAlertsConfig.alerts);
- $: currentModalAlert = filteredModalAlerts[currentIndex] as BottomModalAlertItem;
+ const filteredModalAlerts = $derived(filterModalAlerts(bottomModalAlerts, page.route.id));
- $: hasOnlyPrimaryCta = typeof currentModalAlert?.learnMore === 'undefined';
+ const currentModalAlert = $derived(filteredModalAlerts[currentIndex] as BottomModalAlertItem);
- function handleClose() {
- filteredModalAlerts.forEach((alert) => {
+ const hasOnlyPrimaryCta = $derived(typeof currentModalAlert?.learnMore === 'undefined');
+
+ const isOnOnboarding = $derived(page.route.id.includes('(console)/onboarding'));
+
+ function handleClose(id: string | null = null) {
+ const alerts = !id
+ ? filteredModalAlerts
+ : filteredModalAlerts.filter((alert) => alert.id === id);
+
+ alerts.forEach((alert) => {
const modalAlert = alert;
dismissBottomModalAlert(modalAlert.id);
hideNotification(modalAlert.id, { coolOffPeriod: 24 * 365 });
if (modalAlert.closed) modalAlert.closed();
});
+
+ // reset `currentIndex` if we removed the last item
+ if (currentIndex >= filteredModalAlerts.length - 1) {
+ currentIndex = Math.max(0, filteredModalAlerts.length - 2);
+ }
}
function showNext() {
@@ -114,8 +126,13 @@
}
// the button component cannot have both href and on:click!
- function triggerWindowLink(alertAction: BottomModalAlertAction, event?: string) {
+ function triggerWindowLink(alert: BottomModalAlertItem, event?: string) {
+ const alertAction = alert.cta;
const shouldShowUpgrade = showUpgrade();
+
+ // for correct event tracking after removal
+ const currentModalId = currentModalAlert.id;
+
const url = shouldShowUpgrade
? $upgradeURL
: alertAction.link({
@@ -130,14 +147,12 @@
}
if (alertAction?.hideOnClick === true) {
- // be careful of this one.
- // once clicked, its gone!
- handleClose();
+ handleClose(alert.id); // gone!
}
trackEvent(Click.PromoClick, {
- promo: currentModalAlert.id,
- type: shouldShowUpgrade ? 'upgrade' : (event ?? `cta_click_${currentModalAlert.id}`)
+ promo: currentModalId,
+ type: shouldShowUpgrade ? 'upgrade' : (event ?? `cta_click_${currentModalId}`)
});
}
@@ -156,12 +171,10 @@
}
}
- onMount(() => {
- addBottomModalAlerts();
- });
+ onMount(addBottomModalAlerts);
-{#if filteredModalAlerts.length > 0 && currentModalAlert && !page.url.pathname.includes('console/onboarding')}
+{#if !isOnOnboarding && filteredModalAlerts.length > 0 && currentModalAlert}
{@const shouldShowUpgrade = showUpgrade()}
@@ -170,7 +183,7 @@
@@ -243,7 +261,7 @@
fullWidthMobile
secondary={!hasOnlyPrimaryCta}
class={`${hasOnlyPrimaryCta ? 'only-primary-cta' : ''}`}
- on:click={() => triggerWindowLink(currentModalAlert.cta)}>
+ on:click={() => triggerWindowLink(currentModalAlert)}>
{currentModalAlert.cta.text}
@@ -276,7 +294,7 @@
-
+
diff --git a/src/lib/components/permissions/actions.svelte b/src/lib/components/permissions/actions.svelte
index 4bdbb7637..132ee5b40 100644
--- a/src/lib/components/permissions/actions.svelte
+++ b/src/lib/components/permissions/actions.svelte
@@ -12,7 +12,6 @@
export let showTeam: boolean;
export let showLabel: boolean;
export let showCustom: boolean;
- export let hideOnClick: boolean = false;
export let groups: Writable