From f7db048d2b77e6af1a74c25bc64dd7d67d981711 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 1 Nov 2025 18:03:04 +0530 Subject: [PATCH 01/12] add: animation variants. --- .../(suggestions)/empty.svelte | 165 +++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 6453124d0e..667f52b5fe 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -111,6 +111,8 @@ let hasTransitioned = $state(false); let scrollAnimationFrame: number | null = null; + let animation: 'glow' | 'shimmer' | 'both' = 'glow'; + let creatingColumns = $state(false); let selectedColumnId = $state(null); let previousColumnId = $state(null); @@ -1257,7 +1259,13 @@ bind:this={rangeOverlayEl} class="columns-range-overlay" class:no-transition={hasTransitioned && customColumns.length > 0} - class:thinking={$tableColumnSuggestions.thinking || creatingColumns}> + class:thinking={$tableColumnSuggestions.thinking || creatingColumns} + data-anim={animation}> + @@ -1947,6 +1955,133 @@ ); animation: inner-shimmer 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; } + + &[data-anim='glow'], + &[data-anim='both'] { + box-shadow: none; + height: 100% !important; + margin-block-start: unset !important; + // background: transparent; + // main spot for handling the glow animations! + background: linear-gradient( + 135deg, + color-mix(in oklab, #fd366e 1%, transparent) 0%, + color-mix(in oklab, #fe9567 1%, transparent) 100% + ); + + .ai-border-glow { + inset: 0; + + position: absolute; + border-radius: var(--border-radius-S, 8px); + pointer-events: none; + overflow: hidden; + + --border-pink: rgba(253, 54, 110, 0.35); + --border-orange: rgba(254, 149, 103, 0.12); + --glow-main: rgba(253, 54, 110, 0.25); + --glow-secondary: rgba(253, 54, 110, 0.15); + + /* animated gradient ring */ + &::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; /* ring width */ + box-sizing: border-box; + background: linear-gradient( + 120deg, + var(--border-pink) 0%, + var(--border-pink) 25%, + var(--border-orange) 50%, + var(--border-pink) 75%, + var(--border-pink) 100% + ); + background-size: 300% 300%; + animation: borderGlow 12s ease-in-out infinite; + + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + mask-composite: exclude; + } + + .inner { + position: absolute; + inset: 2px; + border-radius: calc(var(--border-radius-S, 8px) - 2px); + background: transparent; + overflow: hidden; + pointer-events: none; + + /* top-left radial glow */ + &::before { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient( + circle at bottom left, + var(--glow-main), + rgba(253, 54, 110, 0) 50% + ); + opacity: 0.2; + animation: glowPulse 6s ease-in-out infinite; + animation-delay: 0s; + pointer-events: none; + border-radius: inherit; + } + + /* top-right radial glow */ + &::after { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient( + circle at top right, + var(--glow-secondary), + rgba(253, 54, 110, 0) 20% + ); + opacity: 0.15; + animation: glowPulse 6s ease-in-out infinite; + animation-delay: 2s; + pointer-events: none; + border-radius: inherit; + } + + /* bottom-left and bottom-right glows */ + .bottom-corners { + position: absolute; + inset: 0; + pointer-events: none; + border-radius: inherit; + box-shadow: + inset 0 -50% 100px -40px var(--glow-main), + inset -50% 0 100px -40px var(--glow-secondary); + animation: glowPulse 6s ease-in-out infinite; + animation-delay: 3s; + } + } + } + } + + /* disable sweep shimmer in glow-only */ + &[data-anim='glow'] { + &::after { + content: none; + } + } + + /* hide glow wrapper entirely in shimmer mode */ + &[data-anim='shimmer'] { + .ai-border-glow { + display: none; + } + } } } @@ -2219,4 +2354,32 @@ // stroke-dashoffset: 67.858; // } //} + + @keyframes borderGlow { + 0% { + background-position: 0% 0%; + } + 25% { + background-position: 100% 0%; + } + 50% { + background-position: 100% 100%; + } + 75% { + background-position: 0% 100%; + } + 100% { + background-position: 0% 0%; + } + } + + @keyframes glowPulse { + 0%, + 100% { + opacity: 0.1; + } + 50% { + opacity: 0.6; + } + } From e405c3cf03caa1e037a83098eb3e8244e82f0cc5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 18:15:18 +0530 Subject: [PATCH 02/12] update: delay. --- .../databases/database-[database]/(suggestions)/empty.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 667f52b5fe..8f34edd652 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -628,6 +628,8 @@ await sleep(NOTIFICATION_AND_MOCK_DELAY); suggestedColumns = mockSuggestions; } else { + await sleep(5000); // for design review on stage + suggestedColumns = (await sdk .forProject(page.params.region, page.params.project) .console.suggestColumns({ From 66b6fa3de3f41ae7fdc1d0d029d29e00e5beda5c Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 18:58:06 +0530 Subject: [PATCH 03/12] update: animations. add: user's row data if it exists. --- .../(suggestions)/empty.svelte | 58 +++++++++++++++---- .../table-[table]/+page.svelte | 2 +- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 8f34edd652..8f570c4ecc 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -44,11 +44,14 @@ import { fade } from 'svelte/transition'; import IconAINotification from './icon/aiNotification.svelte'; + import type { Models } from '@appwrite.io/console'; let { - userColumns = [] + userColumns = [], + userDataRows = [] }: { userColumns?: Column[]; + userDataRows?: Models.Row[]; } = $props(); const tableId = page.params.table; @@ -573,7 +576,9 @@ }; const spreadsheetColumns = $derived(getRowColumns()); - const emptyCells = $derived(($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0)); + const emptyCells = $derived( + ($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0) - userDataRows.length + ); onMount(async () => { columnsOrder.set(preferences.getColumnOrder(tableId)); @@ -625,7 +630,7 @@ try { if (VARS.MOCK_AI_SUGGESTIONS) { /* animation */ - await sleep(NOTIFICATION_AND_MOCK_DELAY); + await sleep(NOTIFICATION_AND_MOCK_DELAY * 100); suggestedColumns = mockSuggestions; } else { await sleep(5000); // for design review on stage @@ -1193,13 +1198,32 @@ // get all custom column IDs const suggestedColumnIds = customColumns.map((col) => col.key); + const firstSuggestedColumnId = suggestedColumnIds[0]; + + const columnBeforeOverlay = + staticUserColumns.length > 0 + ? staticUserColumns[staticUserColumns.length - 1].id + : '$id'; + const allCells = spreadsheetContainer.querySelectorAll('[role="cell"][data-column-id]'); allCells.forEach((cell) => { const columnId = cell.getAttribute('data-column-id'); if (columnId && suggestedColumnIds.includes(columnId)) { cell.setAttribute('data-suggested-column', 'true'); + if (columnId === firstSuggestedColumnId) { + cell.setAttribute('data-first-suggested-column', 'true'); + } else { + cell.removeAttribute('data-first-suggested-column'); + } } else { cell.removeAttribute('data-suggested-column'); + cell.removeAttribute('data-first-suggested-column'); + } + + if (columnId === columnBeforeOverlay) { + cell.setAttribute('data-column-before-overlay', 'true'); + } else { + cell.removeAttribute('data-column-before-overlay'); } }); }); @@ -1505,6 +1529,16 @@ {/each} + {#each userDataRows as row} + + {#each spreadsheetColumns as column} + + {row[column.id] ?? ''} + + {/each} + + {/each} + {#each Array.from({ length: emptyCells }) as _} {#each spreadsheetColumns as column} @@ -1810,7 +1844,7 @@ overflow: visible; scrollbar-width: none; - --columns-range-pink-border-color: rgba(253, 54, 110, 0.24); + --columns-range-pink-border-color: rgba(253, 54, 110, 0.18); --columns-range-pink-header-background-color: rgba(253, 54, 110, 0.12); &.custom-columns { @@ -1839,6 +1873,14 @@ transition: border-color 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); } + :global([role='cell'][data-column-before-overlay='true'] .column-resizer-disabled) { + opacity: 0 !important; + } + + :global([role='cell'][data-column-id='actions']) { + border-left: none !important; + } + :global( [role='cell'][data-suggested-column='true']:has(+ [data-column-id='actions']) .column-resizer-disabled @@ -1965,11 +2007,7 @@ margin-block-start: unset !important; // background: transparent; // main spot for handling the glow animations! - background: linear-gradient( - 135deg, - color-mix(in oklab, #fd366e 1%, transparent) 0%, - color-mix(in oklab, #fe9567 1%, transparent) 100% - ); + background: rgba(253, 54, 110, 0.04); .ai-border-glow { inset: 0; @@ -2165,7 +2203,7 @@ } & :global([data-select='true']) { - opacity: 0.85; + // opacity: 0.85; pointer-events: none; } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index 1c1c256f85..f066514a03 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -266,7 +266,7 @@ {/if} {:else if isCloud && canShowSuggestionsSheet} - + {:else} {#snippet subtitle()} From 17d7fc200affbddfbfdd6d812208ef164eecde7a Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 21:20:05 +0530 Subject: [PATCH 04/12] fix: ai icon. --- .../database-[database]/(suggestions)/icon/ai.svelte | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte index 1d51db1688..9fae0e544b 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte @@ -71,8 +71,16 @@ border: 1.25px solid rgba(253, 54, 110, 0.12); padding: 5px 0; + min-width: 40px; width: 40px !important; height: 40px !important; + + & svg { + width: 30px; + height: 30px; + flex-shrink: 0; + aspect-ratio: 1/1; + } } :global(.ai-icon-holder.notification) { From 3a65420f8cd974fa1be663ae294ffbe09285f0ea Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 21:20:45 +0530 Subject: [PATCH 05/12] fix: area not clickable. fix: looong sleep/wait on mock suggestions lol. --- .../(suggestions)/empty.svelte | 77 +++++++++++-------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 8f570c4ecc..a11563025f 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -630,7 +630,7 @@ try { if (VARS.MOCK_AI_SUGGESTIONS) { /* animation */ - await sleep(NOTIFICATION_AND_MOCK_DELAY * 100); + await sleep(NOTIFICATION_AND_MOCK_DELAY); suggestedColumns = mockSuggestions; } else { await sleep(5000); // for design review on stage @@ -1532,8 +1532,15 @@ {#each userDataRows as row} {#each spreadsheetColumns as column} + {@const columnObj = getColumn(column.id)} + {@const interactable = + isCustomColumn(column.id) && columnObj && !columnObj.isPlaceholder} - {row[column.id] ?? ''} + {@render rowCellInteractiveButton({ + interactable, + column, + row + })} {/each} @@ -1543,37 +1550,10 @@ {#each spreadsheetColumns as column} {@const columnObj = getColumn(column.id)} - {@const isColumnInteractable = + {@const interactable = isCustomColumn(column.id) && columnObj && !columnObj.isPlaceholder} - + {@render rowCellInteractiveButton({ interactable, column })} {/each} @@ -1756,6 +1736,41 @@ {/snippet}--> +{#snippet rowCellInteractiveButton({ interactable, column, row = null })} + +{/snippet} + {#snippet changeColumnTypePopover({ id, columnObj, iconColor, icon, isColumnInteractable, index })}
Date: Sun, 2 Nov 2025 21:54:14 +0530 Subject: [PATCH 06/12] fix: area not clickable. fix: looong sleep/wait on mock suggestions lol. fix: incorrect layout of fake cells. --- .../(suggestions)/empty.svelte | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index a11563025f..50a2fa15af 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -82,6 +82,7 @@ return { ...column, width: getUserColumnWidth(column.id, defaultWidth), + custom: false, resizable: false, draggable: false }; @@ -132,12 +133,17 @@ let columnBeingDeleted: (SuggestedColumnSchema & { deletedIndex?: number }) | null = $state(null); - const baseColProps = { draggable: false, resizable: false }; + const baseColProps = { + custom: false, + draggable: false, + resizable: false + }; const NOTIFICATION_AND_MOCK_DELAY = 1250; const COLUMN_DELETION_UNDO_TIMER_LIMIT = 10000; // 10 seconds const getColumnWidth = (columnKey: string) => Math.max(180, columnKey.length * 8 + 60); + const safeNumericValue = (value: number | undefined) => value !== undefined && isWithinSafeRange(value) ? value : undefined; @@ -517,13 +523,14 @@ width: { min: getColumnWidth(col.key) }, icon: columnOption?.icon, draggable: false, - resizable: false + resizable: false, + custom: true }; }); }); - const getRowColumns = (): Column[] => { - const minColumnWidth = 180; + const getRowColumns = (): (Column & { custom: boolean })[] => { + const minColumnWidth = 250; const fixedWidths = { id: minColumnWidth, actions: 40, selection: 40 }; const equalWidthColumns = [...staticUserColumns, ...customSuggestedColumns]; @@ -1342,11 +1349,9 @@ : '--overlay-icon-color'} {@const isColumnInteractable = isCustomColumn(column.id) && columnObj && !columnObj.isPlaceholder} - {@const isUserColumn = - column.id === '$id' || - staticUserColumns.some((col) => col.id === column.id)} + {@const userColumn = column.id === '$id' || column.custom} - {#if isUserColumn} + {#if userColumn} - {@render rowCellInteractiveButton({ interactable, column })} + {@render rowCellInteractiveButton({ + interactable, + column + })} {/each} @@ -1740,6 +1748,7 @@ {/snippet} @@ -1962,6 +1971,14 @@ width: 100%; height: 100%; cursor: pointer; + + & :global(.fake-cell) { + min-height: 40px; + position: relative; + align-items: center; + font-size: var(--font-size-s); + padding: var(--space-4) var(--space-6); + } } .columns-range-overlay { From 5b87b94ea23872219ba06a52390ce3920e89a3c6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 21:54:22 +0530 Subject: [PATCH 07/12] update: cache on format. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a2e77773d..75f11ee7f9 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "clean": "rm -rf node_modules && rm -rf .svelte_kit && pnpm i --force", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", - "format": "prettier --write .", + "format": "prettier --write --cache .", "lint": "prettier --check . && eslint .", "test": "TZ=EST vitest run", "test:ui": "TZ=EST vitest --ui", From d37201c725c020f6813f16c824faf2bbabf853a0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 21:55:18 +0530 Subject: [PATCH 08/12] fix: userColumn check. --- .../databases/database-[database]/(suggestions)/empty.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 50a2fa15af..c0d2bd0078 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -1349,7 +1349,7 @@ : '--overlay-icon-color'} {@const isColumnInteractable = isCustomColumn(column.id) && columnObj && !columnObj.isPlaceholder} - {@const userColumn = column.id === '$id' || column.custom} + {@const userColumn = column.id === '$id' || !column.custom} {#if userColumn} Date: Fri, 7 Nov 2025 13:03:26 +0530 Subject: [PATCH 09/12] update: animation. --- .../(suggestions)/empty.svelte | 404 ++++++++++-------- 1 file changed, 230 insertions(+), 174 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index c0d2bd0078..7194bd7078 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -115,7 +115,7 @@ let hasTransitioned = $state(false); let scrollAnimationFrame: number | null = null; - let animation: 'glow' | 'shimmer' | 'both' = 'glow'; + let animation: 'legacy' | 'new' = 'new'; let creatingColumns = $state(false); let selectedColumnId = $state(null); @@ -1294,10 +1294,12 @@ class:no-transition={hasTransitioned && customColumns.length > 0} class:thinking={$tableColumnSuggestions.thinking || creatingColumns} data-anim={animation}> - @@ -1829,6 +1831,27 @@ {/snippet} +{#snippet edgeGradients(side: 'left' | 'right')} + + {@const gradientConfigs = [ + { pos: '20%', color: 'var(--border-pink)', spread: '25%', delay: '0s' }, + { pos: '50%', color: 'var(--border-orange)', spread: '15%', delay: '1s' }, + { pos: '80%', color: 'var(--border-pink)', spread: '25%', delay: '2s' }, + { pos: '35%', color: 'var(--border-pink)', spread: '40%', delay: '0.5s' }, + { pos: '65%', color: 'var(--border-orange)', spread: '40%', delay: '1.5s' } + ]} + {@const xPosition = side === 'left' ? '0%' : '100%'} + +
+ {#each gradientConfigs as grad} +
+
+ {/each} +
+{/snippet} + @@ -2007,151 +2030,170 @@ } &.thinking { - margin-block-start: 2px; - height: calc(100% - 4px); + overflow: hidden; border-radius: var(--border-radius-S, 8px); - box-shadow: - 0 0 0 var(--border-width-l, 2px) #fd366e, - inset 0 0 0 1px color-mix(in oklab, #fe9567 20%, transparent); - - transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); - - &::after { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient( - 90deg, - transparent, - rgba(255, 255, 255, 0.8), - transparent - ); - animation: inner-shimmer 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; + + /* Legacy shimmer animation */ + &[data-anim='legacy'] { + margin-block-start: 2px; + height: calc(100% - 4px); + box-shadow: + 0 0 0 var(--border-width-l, 2px) #fd366e, + inset 0 0 0 1px color-mix(in oklab, #fe9567 20%, transparent); + + &::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.8), + transparent + ); + animation: legacy-shimmer 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; + } + + .inner-glow-wrapper { + display: none; + } } - &[data-anim='glow'], - &[data-anim='both'] { - box-shadow: none; - height: 100% !important; - margin-block-start: unset !important; - // background: transparent; - // main spot for handling the glow animations! - background: rgba(253, 54, 110, 0.04); + /* New animation with border glow and edge pulses */ + &[data-anim='new'] { + /* bg wash: 3% subtle | 7% default */ + background: rgba(253, 54, 110, 0.03); + @supports (background: color-mix(in oklab, #fd366e 1%, transparent)) { + background: linear-gradient( + 135deg, + color-mix(in oklab, #fd366e 3%, transparent) 0%, + color-mix(in oklab, #fe9567 3%, transparent) 100% + ); + } + + /* border colors: pink 0.4 | orange 0.25 */ + --border-pink: rgba(253, 54, 110, 0.4); + --border-orange: rgba(254, 149, 103, 0.25); - .ai-border-glow { + /* border ring: thickness 2px | speed 12s | size 300% */ + &::before { + content: ''; + position: absolute; inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient( + 120deg, + var(--border-pink) 0%, + var(--border-orange) 50%, + var(--border-pink) 100% + ); + background-size: 300% 300%; + pointer-events: none; + box-sizing: border-box; + animation: borderGlow 12s ease-in-out infinite; + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + mask-composite: exclude; + } + /* shine sweep: opacity 0.02 | blur 80px | speed 2s | angle -25deg */ + &::after { + content: ''; position: absolute; - border-radius: var(--border-radius-S, 8px); + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + 145deg, + rgba(255, 255, 255, 0) 45%, + rgba(255, 255, 255, 0.02) 50%, + rgba(255, 255, 255, 0) 55% + ); + transform: rotate(-25deg); + filter: blur(80px); pointer-events: none; + animation: shine 2s linear infinite; + } + + .inner-glow-wrapper { + position: absolute; + inset: 2px; + border-radius: calc(var(--border-radius-S, 8px) - 2px); overflow: hidden; + } - --border-pink: rgba(253, 54, 110, 0.35); - --border-orange: rgba(254, 149, 103, 0.12); - --glow-main: rgba(253, 54, 110, 0.25); - --glow-secondary: rgba(253, 54, 110, 0.15); - - /* animated gradient ring */ - &::before { - content: ''; - position: absolute; - inset: 0; - border-radius: inherit; - padding: 2px; /* ring width */ - box-sizing: border-box; - background: linear-gradient( - 120deg, - var(--border-pink) 0%, - var(--border-pink) 25%, - var(--border-orange) 50%, - var(--border-pink) 75%, - var(--border-pink) 100% - ); - background-size: 300% 300%; - animation: borderGlow 12s ease-in-out infinite; - - -webkit-mask: - linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask: - linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - mask-composite: exclude; - } + /* edge glows container */ + .edge-glow { + position: absolute; + inset: 0; + pointer-events: none; + border-radius: inherit; + } - .inner { - position: absolute; - inset: 2px; - border-radius: calc(var(--border-radius-S, 8px) - 2px); - background: transparent; - overflow: hidden; - pointer-events: none; - - /* top-left radial glow */ - &::before { - content: ''; - position: absolute; - inset: 0; - background: radial-gradient( - circle at bottom left, - var(--glow-main), - rgba(253, 54, 110, 0) 50% - ); - opacity: 0.2; - animation: glowPulse 6s ease-in-out infinite; - animation-delay: 0s; - pointer-events: none; - border-radius: inherit; - } + /* edge pulses: speed 6s | opacity 0.15-0.35 (see keyframes) | config in snippet */ + .edge-glow.left .grad, + .edge-glow.right .grad { + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } - /* top-right radial glow */ - &::after { - content: ''; - position: absolute; - inset: 0; - background: radial-gradient( - circle at top right, - var(--glow-secondary), - rgba(253, 54, 110, 0) 20% - ); - opacity: 0.15; - animation: glowPulse 6s ease-in-out infinite; - animation-delay: 2s; - pointer-events: none; - border-radius: inherit; - } + .edge-glow.left .grad { + animation: leftPulse 6s ease-in-out infinite; + } - /* bottom-left and bottom-right glows */ - .bottom-corners { - position: absolute; - inset: 0; - pointer-events: none; - border-radius: inherit; - box-shadow: - inset 0 -50% 100px -40px var(--glow-main), - inset -50% 0 100px -40px var(--glow-secondary); - animation: glowPulse 6s ease-in-out infinite; - animation-delay: 3s; - } - } + .edge-glow.right .grad { + animation: rightPulse 6s ease-in-out infinite; } - } - /* disable sweep shimmer in glow-only */ - &[data-anim='glow'] { - &::after { - content: none; + /* top/bottom ambient: opacity 0.05 | spread 25% | static (no animation) */ + .edge-glow.top { + background: + radial-gradient( + circle at 20% 0%, + rgba(254, 149, 103, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 50% 0%, + rgba(253, 54, 110, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 80% 0%, + rgba(253, 54, 110, 0.05) 0%, + transparent 25% + ); } - } - /* hide glow wrapper entirely in shimmer mode */ - &[data-anim='shimmer'] { - .ai-border-glow { - display: none; + .edge-glow.bottom { + background: + radial-gradient( + circle at 20% 100%, + rgba(254, 149, 103, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 50% 100%, + rgba(253, 54, 110, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 80% 100%, + rgba(254, 149, 103, 0.05) 0%, + transparent 25% + ); } } } @@ -2284,7 +2326,7 @@ ); } - @keyframes inner-shimmer { + @keyframes legacy-shimmer { 0% { left: -100%; opacity: 0; @@ -2301,24 +2343,66 @@ } } + @keyframes leftPulse { + 0%, + 100% { + opacity: 0.15; + } + 33% { + opacity: 0.35; + } + 66% { + opacity: 0.25; + } + } + + @keyframes rightPulse { + 0%, + 100% { + opacity: 0.15; + } + 33% { + opacity: 0.35; + } + 66% { + opacity: 0.25; + } + } + + @keyframes shine { + 0% { + transform: translateX(-100%) rotate(-25deg); + } + 100% { + transform: translateX(100%) rotate(-25deg); + } + } + + @keyframes borderGlow { + 0% { + background-position: 0 0; + } + 25% { + background-position: 100% 0; + } + 50% { + background-position: 100% 100%; + } + 75% { + background-position: 0 100%; + } + 100% { + background-position: 0 0; + } + } + :global(.theme-dark) .spreadsheet-container-outer { --columns-range-pink-header-background-color: unset; --columns-range-pink-border-color: rgba(253, 54, 110, 0.12) !important; } :global(.theme-dark) .columns-range-overlay.thinking { - &::before { - background: linear-gradient( - 90deg, - rgba(253, 54, 110, 0.01), - rgba(254, 149, 103, 0.03), - rgba(253, 54, 110, 0.01), - rgba(254, 149, 103, 0.03), - rgba(253, 54, 110, 0.01) - ); - } - - &::after { + &[data-anim='legacy']::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.04), transparent); } } @@ -2426,32 +2510,4 @@ // stroke-dashoffset: 67.858; // } //} - - @keyframes borderGlow { - 0% { - background-position: 0% 0%; - } - 25% { - background-position: 100% 0%; - } - 50% { - background-position: 100% 100%; - } - 75% { - background-position: 0% 100%; - } - 100% { - background-position: 0% 0%; - } - } - - @keyframes glowPulse { - 0%, - 100% { - opacity: 0.1; - } - 50% { - opacity: 0.6; - } - } From 1d95514147b3f46196aa36871315ff33b6cdb9ac Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 7 Nov 2025 13:14:56 +0530 Subject: [PATCH 10/12] update the shimmer color on new animation for light/dark themes. --- .../database-[database]/(suggestions)/empty.svelte | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 7194bd7078..a4ceee4ae0 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -2105,7 +2105,7 @@ mask-composite: exclude; } - /* shine sweep: opacity 0.02 | blur 80px | speed 2s | angle -25deg */ + /* shine sweep: opacity 0.5 light / 0.02 dark | blur 80px | speed 2s | angle -25deg */ &::after { content: ''; position: absolute; @@ -2116,7 +2116,7 @@ background: linear-gradient( 145deg, rgba(255, 255, 255, 0) 45%, - rgba(255, 255, 255, 0.02) 50%, + rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0) 55% ); transform: rotate(-25deg); @@ -2405,6 +2405,15 @@ &[data-anim='legacy']::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.04), transparent); } + + &[data-anim='new']::after { + background: linear-gradient( + 145deg, + rgba(255, 255, 255, 0) 45%, + rgba(255, 255, 255, 0.02) 50%, + rgba(255, 255, 255, 0) 55% + ); + } } :global(.cell-editor) { From 90c56801defd4202ca0b6b6a420b9a4fc43a4c2f Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 9 Nov 2025 18:18:34 +0530 Subject: [PATCH 11/12] update: animation. --- package.json | 2 +- .../database-[database]/(suggestions)/empty.svelte | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 75f11ee7f9..b4fc20d0fa 100644 --- a/package.json +++ b/package.json @@ -95,5 +95,5 @@ "svelte-preprocess" ] }, - "packageManager": "pnpm@10.15.1" + "packageManager": "pnpm@10.20.0" } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index a4ceee4ae0..02c06bec1f 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -2065,14 +2065,16 @@ /* New animation with border glow and edge pulses */ &[data-anim='new'] { /* bg wash: 3% subtle | 7% default */ - background: rgba(253, 54, 110, 0.03); + /*background: rgba(253, 54, 110, 0.03); @supports (background: color-mix(in oklab, #fd366e 1%, transparent)) { background: linear-gradient( 135deg, color-mix(in oklab, #fd366e 3%, transparent) 0%, color-mix(in oklab, #fe9567 3%, transparent) 100% ); - } + }*/ + + background: transparent; /* border colors: pink 0.4 | orange 0.25 */ --border-pink: rgba(253, 54, 110, 0.4); @@ -2150,11 +2152,11 @@ } .edge-glow.left .grad { - animation: leftPulse 6s ease-in-out infinite; + animation: leftPulse 6s ease-in-out infinite backwards; } .edge-glow.right .grad { - animation: rightPulse 6s ease-in-out infinite; + animation: rightPulse 6s ease-in-out infinite backwards; } /* top/bottom ambient: opacity 0.05 | spread 25% | static (no animation) */ @@ -2346,7 +2348,7 @@ @keyframes leftPulse { 0%, 100% { - opacity: 0.15; + opacity: 0; } 33% { opacity: 0.35; @@ -2359,7 +2361,7 @@ @keyframes rightPulse { 0%, 100% { - opacity: 0.15; + opacity: 0; } 33% { opacity: 0.35; From ad3e23ec2d995e5117d950f332d298fa408b614a Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 11 Nov 2025 19:56:07 +0530 Subject: [PATCH 12/12] cleanup. --- .../(suggestions)/empty.svelte | 289 +++++++----------- 1 file changed, 115 insertions(+), 174 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 02c06bec1f..f7c0b189d2 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -115,8 +115,6 @@ let hasTransitioned = $state(false); let scrollAnimationFrame: number | null = null; - let animation: 'legacy' | 'new' = 'new'; - let creatingColumns = $state(false); let selectedColumnId = $state(null); let previousColumnId = $state(null); @@ -640,8 +638,6 @@ await sleep(NOTIFICATION_AND_MOCK_DELAY); suggestedColumns = mockSuggestions; } else { - await sleep(5000); // for design review on stage - suggestedColumns = (await sdk .forProject(page.params.region, page.params.project) .console.suggestColumns({ @@ -1292,8 +1288,7 @@ bind:this={rangeOverlayEl} class="columns-range-overlay" class:no-transition={hasTransitioned && customColumns.length > 0} - class:thinking={$tableColumnSuggestions.thinking || creatingColumns} - data-anim={animation}> + class:thinking={$tableColumnSuggestions.thinking || creatingColumns}>
{@render edgeGradients('left')} {@render edgeGradients('right')} @@ -2033,170 +2028,122 @@ overflow: hidden; border-radius: var(--border-radius-S, 8px); - /* Legacy shimmer animation */ - &[data-anim='legacy'] { - margin-block-start: 2px; - height: calc(100% - 4px); - box-shadow: - 0 0 0 var(--border-width-l, 2px) #fd366e, - inset 0 0 0 1px color-mix(in oklab, #fe9567 20%, transparent); - - &::after { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient( - 90deg, - transparent, - rgba(255, 255, 255, 0.8), - transparent - ); - animation: legacy-shimmer 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; - } - - .inner-glow-wrapper { - display: none; - } + background: transparent; + + --border-pink: rgba(253, 54, 110, 0.4); + --border-orange: rgba(254, 149, 103, 0.25); + + &::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient( + 120deg, + var(--border-pink) 0%, + var(--border-orange) 50%, + var(--border-pink) 100% + ); + background-size: 300% 300%; + pointer-events: none; + box-sizing: border-box; + animation: borderGlow 12s ease-in-out infinite; + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + mask-composite: exclude; } - /* New animation with border glow and edge pulses */ - &[data-anim='new'] { - /* bg wash: 3% subtle | 7% default */ - /*background: rgba(253, 54, 110, 0.03); - @supports (background: color-mix(in oklab, #fd366e 1%, transparent)) { - background: linear-gradient( - 135deg, - color-mix(in oklab, #fd366e 3%, transparent) 0%, - color-mix(in oklab, #fe9567 3%, transparent) 100% - ); - }*/ - - background: transparent; - - /* border colors: pink 0.4 | orange 0.25 */ - --border-pink: rgba(253, 54, 110, 0.4); - --border-orange: rgba(254, 149, 103, 0.25); - - /* border ring: thickness 2px | speed 12s | size 300% */ - &::before { - content: ''; - position: absolute; - inset: 0; - border-radius: inherit; - padding: 2px; - background: linear-gradient( - 120deg, - var(--border-pink) 0%, - var(--border-orange) 50%, - var(--border-pink) 100% - ); - background-size: 300% 300%; - pointer-events: none; - box-sizing: border-box; - animation: borderGlow 12s ease-in-out infinite; - -webkit-mask: - linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask: - linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - mask-composite: exclude; - } - - /* shine sweep: opacity 0.5 light / 0.02 dark | blur 80px | speed 2s | angle -25deg */ - &::after { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: linear-gradient( - 145deg, - rgba(255, 255, 255, 0) 45%, - rgba(255, 255, 255, 0.5) 50%, - rgba(255, 255, 255, 0) 55% - ); - transform: rotate(-25deg); - filter: blur(80px); - pointer-events: none; - animation: shine 2s linear infinite; - } + &::after { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + 145deg, + rgba(255, 255, 255, 0) 45%, + rgba(255, 255, 255, 0.5) 50%, + rgba(255, 255, 255, 0) 55% + ); + transform: rotate(-25deg); + filter: blur(80px); + pointer-events: none; + animation: shine 2s linear infinite; + } - .inner-glow-wrapper { - position: absolute; - inset: 2px; - border-radius: calc(var(--border-radius-S, 8px) - 2px); - overflow: hidden; - } + .inner-glow-wrapper { + position: absolute; + inset: 2px; + border-radius: calc(var(--border-radius-S, 8px) - 2px); + overflow: hidden; + } - /* edge glows container */ - .edge-glow { - position: absolute; - inset: 0; - pointer-events: none; - border-radius: inherit; - } + .edge-glow { + position: absolute; + inset: 0; + pointer-events: none; + border-radius: inherit; + } - /* edge pulses: speed 6s | opacity 0.15-0.35 (see keyframes) | config in snippet */ - .edge-glow.left .grad, - .edge-glow.right .grad { - position: absolute; - inset: 0; - border-radius: inherit; - pointer-events: none; - } + .edge-glow.left .grad, + .edge-glow.right .grad { + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } - .edge-glow.left .grad { - animation: leftPulse 6s ease-in-out infinite backwards; - } + .edge-glow.left .grad { + animation: leftPulse 6s ease-in-out infinite backwards; + } - .edge-glow.right .grad { - animation: rightPulse 6s ease-in-out infinite backwards; - } + .edge-glow.right .grad { + animation: rightPulse 6s ease-in-out infinite backwards; + } - /* top/bottom ambient: opacity 0.05 | spread 25% | static (no animation) */ - .edge-glow.top { - background: - radial-gradient( - circle at 20% 0%, - rgba(254, 149, 103, 0.05) 0%, - transparent 25% - ), - radial-gradient( - circle at 50% 0%, - rgba(253, 54, 110, 0.05) 0%, - transparent 25% - ), - radial-gradient( - circle at 80% 0%, - rgba(253, 54, 110, 0.05) 0%, - transparent 25% - ); - } + .edge-glow.top { + background: + radial-gradient( + circle at 20% 0%, + rgba(254, 149, 103, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 50% 0%, + rgba(253, 54, 110, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 80% 0%, + rgba(253, 54, 110, 0.05) 0%, + transparent 25% + ); + } - .edge-glow.bottom { - background: - radial-gradient( - circle at 20% 100%, - rgba(254, 149, 103, 0.05) 0%, - transparent 25% - ), - radial-gradient( - circle at 50% 100%, - rgba(253, 54, 110, 0.05) 0%, - transparent 25% - ), - radial-gradient( - circle at 80% 100%, - rgba(254, 149, 103, 0.05) 0%, - transparent 25% - ); - } + .edge-glow.bottom { + background: + radial-gradient( + circle at 20% 100%, + rgba(254, 149, 103, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 50% 100%, + rgba(253, 54, 110, 0.05) 0%, + transparent 25% + ), + radial-gradient( + circle at 80% 100%, + rgba(254, 149, 103, 0.05) 0%, + transparent 25% + ); } } } @@ -2403,19 +2350,13 @@ --columns-range-pink-border-color: rgba(253, 54, 110, 0.12) !important; } - :global(.theme-dark) .columns-range-overlay.thinking { - &[data-anim='legacy']::after { - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.04), transparent); - } - - &[data-anim='new']::after { - background: linear-gradient( - 145deg, - rgba(255, 255, 255, 0) 45%, - rgba(255, 255, 255, 0.02) 50%, - rgba(255, 255, 255, 0) 55% - ); - } + :global(.theme-dark) .columns-range-overlay.thinking::after { + background: linear-gradient( + 145deg, + rgba(255, 255, 255, 0) 45%, + rgba(255, 255, 255, 0.02) 50%, + rgba(255, 255, 255, 0) 55% + ); } :global(.cell-editor) {