From 498ba4d8c6f08830da3bf78870b86fa4a50a5964 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 2 Oct 2025 10:41:31 +0530 Subject: [PATCH 01/70] update: make empty sheet interactive [wip]. --- .../table-[table]/layout/emptySheet.svelte | 216 ++++++++++++------ 1 file changed, 149 insertions(+), 67 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte index bdc92fb75..b5a1027b9 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte @@ -94,53 +94,110 @@ const getCustomColumns = (): Column[] => customColumns.map((col: Column) => ({ ...col, - width: 180, hide: false, icon: columnOptions.find((colOpt) => colOpt.type === col?.type)?.icon, ...baseColProps })); - const getRowColumns = (): Column[] => [ - { - id: '$id', - title: '$id', - type: 'string', - width: 180, - icon: IconFingerPrint, - ...baseColProps - }, - ...getCustomColumns(), - { - id: '$createdAt', - title: '$createdAt', - type: 'datetime', - width: 180, - icon: IconCalendar, - ...baseColProps - }, - { - id: '$updatedAt', - title: '$updatedAt', - type: 'datetime', - width: 180, - icon: IconCalendar, - ...baseColProps - }, - { - id: 'actions', - title: '', - type: 'string', - icon: IconPlus, - width: customColumns.length ? 555 : 832, - ...baseColProps - }, - { - id: 'empty', - title: '', - type: 'string', - ...baseColProps + const getRowColumns = (): Column[] => { + const minColumnWidth = 180; + const fixedWidths = { id: 180, actions: 40 }; + const hasCustomColumns = customColumns.length > 0; + + const customColumnsData = getCustomColumns(); + + // Calculate column widths based on whether we have custom columns + let columnWidths = { + id: fixedWidths.id, + createdAt: fixedWidths.id, + updatedAt: fixedWidths.id, + custom: minColumnWidth, + actions: hasCustomColumns ? fixedWidths.actions : 1387 + }; + + if (hasCustomColumns) { + const equalWidthColumns = [ + ...customColumnsData, + { id: '$createdAt' }, + { id: '$updatedAt' } + ]; + + const totalBaseWidth = + fixedWidths.id + fixedWidths.actions + equalWidthColumns.length * minColumnWidth; + + const viewportWidth = + spreadsheetContainer?.clientWidth || + (typeof window !== 'undefined' ? window.innerWidth : totalBaseWidth); + + const excessSpace = Math.max(0, viewportWidth - totalBaseWidth); + const extraPerColumn = + equalWidthColumns.length > 0 ? excessSpace / equalWidthColumns.length : 0; + const distributedWidth = minColumnWidth + extraPerColumn; + + columnWidths.createdAt = distributedWidth; + columnWidths.updatedAt = distributedWidth; + columnWidths.custom = distributedWidth; } - ]; + + const columns: Column[] = [ + { + id: '$id', + title: '$id', + type: 'string', + width: columnWidths.id, + icon: IconFingerPrint, + ...baseColProps + } + ]; + + if (hasCustomColumns) { + columns.push( + ...customColumnsData.map((col) => ({ + ...col, + width: columnWidths.custom + })) + ); + } + + columns.push( + { + id: '$createdAt', + title: '$createdAt', + type: 'datetime', + width: columnWidths.createdAt, + icon: IconCalendar, + ...baseColProps + }, + { + id: '$updatedAt', + title: '$updatedAt', + type: 'datetime', + width: columnWidths.updatedAt, + icon: IconCalendar, + ...baseColProps + }, + { + id: 'actions', + title: '', + type: 'string', + icon: IconPlus, + isAction: hasCustomColumns, + width: columnWidths.actions, + ...baseColProps + } + ); + + if (!hasCustomColumns) { + columns.push({ + id: 'empty', + title: '', + type: 'string', + ...baseColProps + }); + } + + return columns; + }; const getIndexesColumns = (): Column[] => [ @@ -165,6 +222,7 @@
0} data-mode={mode} bind:this={spreadsheetContainer}> @@ -179,20 +237,23 @@ }}> {#each spreadsheetColumns as column (column.id)} - {@const columnActionsById = column.id === 'actions'} - -
{ - if (columnActionsById && mode === 'rows') { - $showCreateColumnSheet.show = true; - $showCreateColumnSheet.title = 'Create column'; - $showCreateColumnSheet.columns = $tableColumns; - $showCreateColumnSheet.columnsOrder = $columnsOrder; - } - }}> + {#if column.isAction} + + { + if (mode === 'rows') { + $showCreateColumnSheet.show = true; + $showCreateColumnSheet.title = 'Create column'; + $showCreateColumnSheet.columns = $tableColumns; + $showCreateColumnSheet.columnsOrder = $columnsOrder; + } + }}> + + + + {:else} {/if} -
+ {/if} {/each}
@@ -236,6 +297,7 @@ {#if !$spreadsheetLoading}
0} data-collapsed-tabs={!$expandTabs} style:--dynamic-overlay-height={dynamicOverlayHeight}>
@@ -291,6 +353,31 @@ position: fixed; overflow: hidden; + & :global(.spreadsheet-container) { + overflow-x: auto; + overflow-y: auto; + } + + & :global([data-select='true']) { + opacity: 0.85; + pointer-events: none; + } + + &.custom-columns { + width: unset; + } + + &:not(.custom-columns) :global(.spreadsheet-container) { + overflow-x: hidden; + overflow-y: hidden; + } + + /* alternative selector for header selection */ + & :global(.sticky-header [data-select='true']) { + opacity: 1; + pointer-events: none; + } + &[data-mode='rows'] { & :global([role='rowheader'] :nth-last-child(2) [role='presentation']) { display: none; @@ -306,16 +393,6 @@ } } } - - & :global(.spreadsheet-container) { - overflow-x: hidden; - overflow-y: hidden; - } - - & :global([data-select='true']) { - opacity: 0.85; - pointer-events: none; - } } .spreadsheet-fade-bottom { @@ -342,6 +419,10 @@ @media (min-width: 1024px) { height: var(--dynamic-overlay-height, 70.35vh); } + + &.custom-columns { + pointer-events: none; + } } :global(.theme-dark) .spreadsheet-fade-bottom { @@ -358,6 +439,7 @@ left: 50%; bottom: 35%; position: fixed; + pointer-events: auto; @media (max-width: 768px) and (max-height: 768px) { left: unset; @@ -366,7 +448,7 @@ @media (max-width: 768px) and (max-height: 1024px) { left: unset; - bottom: 15% !important; + bottom: 20% !important; } @media (max-width: 1024px) and (max-height: 1024px) { From a53d5cc12a7858cef4e57f948b953db71c636907 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 2 Oct 2025 13:21:50 +0530 Subject: [PATCH 02/70] update: column selection for delete! --- .../(suggestions)/empty.svelte | 166 ++++++++++++++++-- .../(suggestions)/options.svelte | 6 +- 2 files changed, 156 insertions(+), 16 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 dc7b96e4b..b61a8b303 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 @@ -38,6 +38,7 @@ import Options from './options.svelte'; import { InputSelect, InputText } from '$lib/elements/forms'; import { isCloud, VARS } from '$lib/system'; + import { fade } from 'svelte/transition'; import IconAINotification from './icon/aiNotification.svelte'; @@ -58,6 +59,8 @@ let scrollAnimationFrame: number | null = null; let creatingColumns = $state(false); + let selectedColumnId = $state(null); + let previousColumnId = $state(null); const baseColProps = { draggable: false, resizable: false }; const NOTIFICATION_AND_MOCK_DELAY = 1250; @@ -136,7 +139,8 @@ const hasRealColumns = customColumns.some((col) => !col.isPlaceholder); if (!hasRealColumns) { - // For placeholders or no columns, position overlay to cover custom columns area + // for placeholders or no columns, + // position overlay to cover custom columns area const idCell = getById('$id'); const actionsCell = headerElement!.querySelector( '[role="cell"][data-column-id="actions"]' @@ -266,6 +270,7 @@ const recalcAll = () => { updateOverlayHeight(); updateOverlayBounds(); + updateColumnHighlight(); }; /** @@ -681,6 +686,93 @@ }; } + const handleGlobalClick = (event: MouseEvent) => { + // check if click is outside spreadsheet header area + const target = event.target as HTMLElement; + if (!target?.closest('[role="rowheader"]')) { + selectedColumnId = null; + previousColumnId = null; // reset so next selection won't slide + } + }; + + const updateColumnHighlight = () => { + if (!spreadsheetContainer || !selectedColumnId) return; + + const headerCell = spreadsheetContainer.querySelector( + `[role="rowheader"] [role="cell"][data-column-id="${selectedColumnId}"]` + ); + + if (!headerCell) return; + + // calculate position similar to columns-range-overlay logic + if (!headerElement || !headerElement.isConnected) { + headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + } + + if (!headerElement) return; + + const containerRect = spreadsheetContainer.getBoundingClientRect(); + const cellRect = headerCell.getBoundingClientRect(); + + const left = Math.round(cellRect.left - containerRect.left); + const width = cellRect.width; + + spreadsheetContainer.style.setProperty('--highlight-left', `${left - 2}px`); + spreadsheetContainer.style.setProperty('--highlight-width', `${width + 2}px`); + }; + + $effect(() => { + if (!spreadsheetContainer) return; + + // remove existing hide-border classes + const hiddenCells = spreadsheetContainer.querySelectorAll('[role="cell"].hide-border'); + hiddenCells.forEach((cell) => cell.classList.remove('hide-border')); + + if (!selectedColumnId) return; + + // hide borders for selected column and previous column + const selectedCells = spreadsheetContainer.querySelectorAll( + `[role="cell"][data-column-id="${selectedColumnId}"]` + ); + + selectedCells.forEach((cell) => cell.classList.add('hide-border')); + + // find and hide previous column's borders (which create the left edge of selected column) + const allHeaders = Array.from( + spreadsheetContainer.querySelectorAll( + '[role="rowheader"] [role="cell"][data-column-id]' + ) + ); + const selectedIndex = allHeaders.findIndex( + (cell) => cell.getAttribute('data-column-id') === selectedColumnId + ); + + if (selectedIndex > 0) { + const prevColumnId = allHeaders[selectedIndex - 1].getAttribute('data-column-id'); + if (prevColumnId) { + const previousCells = spreadsheetContainer.querySelectorAll( + `[role="cell"][data-column-id="${prevColumnId}"]` + ); + previousCells.forEach((cell) => cell.classList.add('hide-border')); + } + } + + // update position + updateColumnHighlight(); + + // track for next selection - + // but only if we had a `real` previous selection + if (previousColumnId !== null) { + previousColumnId = selectedColumnId; + } else { + // fresh after a deselect + // set it for future switches + tick().then(() => { + previousColumnId = selectedColumnId; + }) + } + }); + onDestroy(() => { resizeObserver?.disconnect(); hScroller?.removeEventListener('scroll', recalcAllThrottled); @@ -693,7 +785,7 @@ }); - +
0} class:thinking={$tableColumnSuggestions.thinking || creatingColumns}>
+ + + {#if selectedColumnId} +
+
+ {/if}
@@ -729,9 +833,9 @@ {:else} {@const columnObj = getColumn(column.id)} - {@const columnIcon = basicColumnOptions.find( + {@const columnIconColor = !columnObj?.type ? '--non-overlay-icon-color' : '--overlay-icon-color'} @@ -740,12 +844,16 @@ + onShowStateChanged={onPopoverShowStateChanged} + onChildrenClick={() => { + if (isColumnInteractable && !$isTabletViewport) { + selectedColumnId = column.id; + } + }}> {#snippet children(toggle)} { // tablet viewport check because context-menu @@ -839,14 +947,14 @@ required bind:value={columnObj.key} pattern="^[A-Za-z0-9][A-Za-z0-9._\-]*$"> - +
{/if} @@ -1026,6 +1134,36 @@ } } + .column-highlight-overlay { + opacity: 0; + z-index: 10; + position: absolute; + pointer-events: none; + animation: fadeIn 0.2s ease-out forwards; + border-radius: var(--border-radius-s, 4px); + border: var(--border-width-l, 2px) solid rgba(253, 54, 110, 0.6); + + &.slide { + transition: + left 0.3s ease-out, + width 0.3s ease-out; + } + } + + @keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + /* Hide selected column borders when overlay is active - targeted via JS */ + & :global([role='cell'].hide-border .column-resizer-disabled) { + display: none !important; + } + .columns-range-overlay { position: absolute; left: var(--group-left, 0px); @@ -1082,11 +1220,11 @@ & .floating-action-wrapper { & :global(:first-child) { z-index: 21; - left: calc(65% - 525px / 2); + left: calc(65% - 480px / 2); transition: all 600ms cubic-bezier(0.4, 0, 0.2, 1); @media (max-width: 1024px) { - left: calc(50% - 525px / 2); + left: calc(50% - 480px / 2); } @media (max-width: 768px) { @@ -1096,11 +1234,11 @@ } &.expanded :global(:first-child) { - left: calc(60% - 525px / 2); - max-width: 525px !important; + left: calc(60% - 480px / 2); + max-width: 480px !important; @media (max-width: 1024px) { - left: calc(50% - 525px / 2); + left: calc(50% - 480px / 2); } @media (max-width: 768px) { @@ -1110,7 +1248,7 @@ } &.creating-columns :global(:first-child) { - left: calc(67.5% - 525px / 2); + left: calc(67.5% - 480px / 2); max-width: 300px !important; @media (max-width: 1024px) { diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte index 9f6ca1691..ce4827dba 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte @@ -9,13 +9,15 @@ tooltipChildren, toggleOnTapClick = true, onShowStateChanged = null, - enabled = true + enabled = true, + onChildrenClick }: { children: Snippet<[toggle: (event: Event) => void]>; tooltipChildren: Snippet<[toggle: (event: Event) => void]>; toggleOnTapClick?: boolean; onShowStateChanged?: (showing: boolean) => void; enabled?: boolean; + onChildrenClick?: () => void; } = $props(); let showSheet = $state(false); @@ -36,7 +38,7 @@ {@render children(() => (showSheet = false))} {:else} - {/if} From 106b529f0534adee443f2c31bb2b7e760ebd3a82 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 2 Oct 2025 13:22:17 +0530 Subject: [PATCH 03/70] lint. --- .../database-[database]/(suggestions)/empty.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 b61a8b303..919033bf7 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 @@ -769,7 +769,7 @@ // set it for future switches tick().then(() => { previousColumnId = selectedColumnId; - }) + }); } }); @@ -1143,11 +1143,11 @@ border-radius: var(--border-radius-s, 4px); border: var(--border-width-l, 2px) solid rgba(253, 54, 110, 0.6); - &.slide { - transition: + &.slide { + transition: left 0.3s ease-out, width 0.3s ease-out; - } + } } @keyframes fadeIn { From c4c7472bca8a17f5db514b603d5cb053b441816e Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 2 Oct 2025 13:24:03 +0530 Subject: [PATCH 04/70] fix: previousColumnId setter. --- .../databases/database-[database]/(suggestions)/empty.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 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 919033bf7..5563fb27e 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 @@ -767,9 +767,7 @@ } else { // fresh after a deselect // set it for future switches - tick().then(() => { - previousColumnId = selectedColumnId; - }); + setTimeout(() => previousColumnId = selectedColumnId, 25); } }); From dec5acdef68b3170780663ee8766f8b189461bd1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 2 Oct 2025 18:15:17 +0530 Subject: [PATCH 05/70] add: delete support as per new designs. --- .../(suggestions)/empty.svelte | 235 +++++++++++++++--- 1 file changed, 194 insertions(+), 41 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 5563fb27e..36f81b1bb 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 @@ -8,7 +8,8 @@ Spreadsheet, Typography, FloatingActionBar, - Popover + Popover, + Badge } from '@appwrite.io/pink-svelte'; import { IconFingerPrint, IconPlus } from '@appwrite.io/pink-icons-svelte'; import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport'; @@ -60,6 +61,7 @@ let creatingColumns = $state(false); let selectedColumnId = $state(null); + let selectedColumnName = $state(null); let previousColumnId = $state(null); const baseColProps = { draggable: false, resizable: false }; @@ -262,11 +264,44 @@ directAccessScroller.scrollTo({ left: Math.max(0, scrollLeft), - behavior: 'smooth' + behavior: 'instant', }); } }; + function updateColumnHighlight() { + if (!spreadsheetContainer || !selectedColumnId) return; + + const headerCell = spreadsheetContainer.querySelector( + `[role="rowheader"] [role="cell"][data-column-id="${selectedColumnId}"]` + ); + + if (!headerCell) return; + + // calculate position similar to columns-range-overlay logic + if (!headerElement || !headerElement.isConnected) { + headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + } + + if (!headerElement) return; + + const containerRect = spreadsheetContainer.getBoundingClientRect(); + const cellRect = headerCell.getBoundingClientRect(); + + const left = Math.round(cellRect.left - containerRect.left); + const width = cellRect.width; + + spreadsheetContainer.style.setProperty('--highlight-left', `${left - 2}px`); + spreadsheetContainer.style.setProperty('--highlight-width', `${width + 2}px`); + } + + const handleGlobalClick = (event: MouseEvent) => { + const target = event.target as HTMLElement; + if (!target?.closest('[role="rowheader"]')) { + resetSelectedColumn(); + } + }; + const recalcAll = () => { updateOverlayHeight(); updateOverlayBounds(); @@ -281,6 +316,12 @@ scrollAnimationFrame = requestAnimationFrame(() => { recalcAll(); + + // check if selected column is still visible after scroll + if (selectedColumnId && !isColumnVisible(selectedColumnId)) { + resetSelectedColumn(); + } + scrollAnimationFrame = null; }); }; @@ -384,6 +425,9 @@ $tableColumnSuggestions.context = null; $tableColumnSuggestions.thinking = false; + + // reset selection! + resetSelectedColumn(); } async function suggestColumns() { @@ -497,6 +541,9 @@ hScroller.scrollLeft = currentScrollLeft; } }); + + // reset selection! + resetSelectedColumn(); } function updateColumn(columnId: string, updates: Partial) { @@ -520,6 +567,80 @@ return !['$id', '$createdAt', '$updatedAt', 'actions'].includes(id); } + function resetSelectedColumn() { + selectedColumnId = null; + previousColumnId = null; + selectedColumnName = null; + } + + function isColumnVisible(columnId: string) { + if (!spreadsheetContainer || !hScroller) return true; + + const columnCell = spreadsheetContainer.querySelector( + `[role="rowheader"] [role="cell"][data-column-id="${columnId}"]` + ); + + if (!columnCell) return false; + + const cellRect = columnCell.getBoundingClientRect(); + const scrollerRect = hScroller.getBoundingClientRect(); + + // stickies have 40px width + const STICKY_COLUMN_WIDTH = 40; + + // calculate available viewport bounds (excluding both 40px sticky columns) + const leftBound = scrollerRect.left + STICKY_COLUMN_WIDTH; // Selection column (40px) + const rightBound = scrollerRect.right - STICKY_COLUMN_WIDTH; // Actions column (40px) + + const safetyMargin = 2; + return ( + cellRect.left >= leftBound - safetyMargin && cellRect.right <= rightBound + safetyMargin + ); + } + + function scrollColumnIntoView(columnId: string) { + if (!spreadsheetContainer || !hScroller) return false; + + const columnCell = spreadsheetContainer.querySelector( + `[role="rowheader"] [role="cell"][data-column-id="${columnId}"]` + ); + + if (!columnCell) return false; + + const cellRect = columnCell.getBoundingClientRect(); + const scrollerRect = hScroller.getBoundingClientRect(); + + // calculate scroll needed to center the column in view + const scrollLeft = + hScroller.scrollLeft + + cellRect.left - + scrollerRect.left - + (scrollerRect.width - cellRect.width) / 2; + + hScroller.scrollTo({ + left: Math.max(0, scrollLeft), + behavior: 'smooth', + }); + + return true; + } + + function deleteColumn() { + if (!selectedColumnId) return; + + // remove the selected column from customColumns + const columnIndex = customColumns.findIndex((col) => col.key === selectedColumnId); + if (columnIndex !== -1) { + customColumns = customColumns.filter((_, index) => index !== columnIndex); + } + + // reset selection! + resetSelectedColumn(); + + // recalculate view after deletion + requestAnimationFrame(() => recalcAll()); + } + function showIndexSuggestionsNotification() { // safeguard anyways! if (!isCloud) return; @@ -686,40 +807,17 @@ }; } - const handleGlobalClick = (event: MouseEvent) => { - // check if click is outside spreadsheet header area - const target = event.target as HTMLElement; - if (!target?.closest('[role="rowheader"]')) { - selectedColumnId = null; - previousColumnId = null; // reset so next selection won't slide + // scroll to view if needed and select! + function selectColumnWithId(column: Column) { + const columnId = column.id; + selectedColumnName = column.title; + if (!isColumnVisible(columnId)) { + scrollColumnIntoView(columnId); + setTimeout(() => (selectedColumnId = columnId), 300); + } else { + selectedColumnId = columnId; } - }; - - const updateColumnHighlight = () => { - if (!spreadsheetContainer || !selectedColumnId) return; - - const headerCell = spreadsheetContainer.querySelector( - `[role="rowheader"] [role="cell"][data-column-id="${selectedColumnId}"]` - ); - - if (!headerCell) return; - - // calculate position similar to columns-range-overlay logic - if (!headerElement || !headerElement.isConnected) { - headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); - } - - if (!headerElement) return; - - const containerRect = spreadsheetContainer.getBoundingClientRect(); - const cellRect = headerCell.getBoundingClientRect(); - - const left = Math.round(cellRect.left - containerRect.left); - const width = cellRect.width; - - spreadsheetContainer.style.setProperty('--highlight-left', `${left - 2}px`); - spreadsheetContainer.style.setProperty('--highlight-width', `${width + 2}px`); - }; + } $effect(() => { if (!spreadsheetContainer) return; @@ -767,7 +865,7 @@ } else { // fresh after a deselect // set it for future switches - setTimeout(() => previousColumnId = selectedColumnId, 25); + setTimeout(() => (previousColumnId = selectedColumnId), 25); } }); @@ -845,7 +943,7 @@ onShowStateChanged={onPopoverShowStateChanged} onChildrenClick={() => { if (isColumnInteractable && !$isTabletViewport) { - selectedColumnId = column.id; + selectColumnWithId(column); } }}> {#snippet children(toggle)} @@ -891,6 +989,7 @@ !$isTabletViewport ) { toggle(event); + resetSelectedColumn(); } }}> {#if !columnObj?.isPlaceholder} @@ -1048,10 +1147,45 @@
{:else if customColumns.some((col) => !col.isPlaceholder) && showFloatingBar} + +
+ + + + + column selected + + + + + + (selectedColumnId = null)}> + Cancel + + !col.isPlaceholder).length <= 1} + on:click={deleteColumn}> + Delete + + + + +
+ +
+ class:creating-columns={creatingColumns} + class:has-selection={selectedColumnId !== null}> @@ -1216,8 +1350,12 @@ } & .floating-action-wrapper { - & :global(:first-child) { - z-index: 21; + // probably limited, but has good support + // for height transition with fit-content and auto, etc. + interpolate-size: allow-keywords; + + & :global(div:first-of-type) { + z-index: 22; left: calc(65% - 480px / 2); transition: all 600ms cubic-bezier(0.4, 0, 0.2, 1); @@ -1231,7 +1369,7 @@ } } - &.expanded :global(:first-child) { + &.expanded :global(div:first-of-type) { left: calc(60% - 480px / 2); max-width: 480px !important; @@ -1245,6 +1383,21 @@ } } + &.expanded.has-selection :global(div:first-of-type) { + height: 44px; + } + + &.selection :global(div:first-of-type) { + z-index: 21; + bottom: 80px; /* oddly specific maybe, but diff as per design */ + height: fit-content; + padding-bottom: 10px; + border-bottom: unset; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + background: var(--bgcolor-neutral-default); + } + &.creating-columns :global(:first-child) { left: calc(67.5% - 480px / 2); max-width: 300px !important; From 963e3e68c5b2d8393eac46aa40d0a8fc5f9a7dfb Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 2 Oct 2025 19:15:25 +0530 Subject: [PATCH 06/70] add: delete for mobile. --- .../(suggestions)/empty.svelte | 86 ++++++++++++------- .../(suggestions)/options.svelte | 7 ++ 2 files changed, 61 insertions(+), 32 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 36f81b1bb..c07686a99 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 @@ -90,7 +90,7 @@ const updateOverlayHeight = () => { if (!spreadsheetContainer) return; if (!headerElement || !headerElement.isConnected) { - headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + headerElement = spreadsheetContainer?.querySelector('[role="rowheader"]'); } if (!headerElement) return; @@ -111,7 +111,7 @@ const updateOverlayBounds = () => { if (!spreadsheetContainer) return; if (!headerElement || !headerElement.isConnected) { - headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + headerElement = spreadsheetContainer?.querySelector('[role="rowheader"]'); } if (!headerElement) return; @@ -264,7 +264,7 @@ directAccessScroller.scrollTo({ left: Math.max(0, scrollLeft), - behavior: 'instant', + behavior: 'instant' }); } }; @@ -522,8 +522,7 @@ } } - function onPopoverShowStateChanged(value: boolean) { - showFloatingBar = !value; + async function updateOverlaysForMobile(value: boolean) { if ($isSmallViewport) { setTimeout(() => { [rangeOverlayEl, fadeBottomOverlayEl].forEach((el) => { @@ -533,6 +532,11 @@ }); }, 0); } + } + + function onPopoverShowStateChanged(value: boolean) { + showFloatingBar = !value; + updateOverlaysForMobile(value); const currentScrollLeft = hScroller?.scrollLeft || 0; @@ -619,17 +623,17 @@ hScroller.scrollTo({ left: Math.max(0, scrollLeft), - behavior: 'smooth', + behavior: 'smooth' }); return true; } - function deleteColumn() { - if (!selectedColumnId) return; + function deleteColumn(columnId: string) { + if (!columnId) return; // remove the selected column from customColumns - const columnIndex = customColumns.findIndex((col) => col.key === selectedColumnId); + const columnIndex = customColumns.findIndex((col) => col.key === columnId); if (columnIndex !== -1) { customColumns = customColumns.filter((_, index) => index !== columnIndex); } @@ -637,6 +641,9 @@ // reset selection! resetSelectedColumn(); + // see overlay is visible after deletion on mobile! + setTimeout(() => updateOverlaysForMobile(false), 150); + // recalculate view after deletion requestAnimationFrame(() => recalcAll()); } @@ -828,32 +835,34 @@ if (!selectedColumnId) return; - // hide borders for selected column and previous column - const selectedCells = spreadsheetContainer.querySelectorAll( - `[role="cell"][data-column-id="${selectedColumnId}"]` - ); + setTimeout(() => { + // hide borders for selected column and previous column + const selectedCells = spreadsheetContainer.querySelectorAll( + `[role="cell"][data-column-id="${selectedColumnId}"]` + ); - selectedCells.forEach((cell) => cell.classList.add('hide-border')); + selectedCells.forEach((cell) => cell.classList.add('hide-border')); - // find and hide previous column's borders (which create the left edge of selected column) - const allHeaders = Array.from( - spreadsheetContainer.querySelectorAll( - '[role="rowheader"] [role="cell"][data-column-id]' - ) - ); - const selectedIndex = allHeaders.findIndex( - (cell) => cell.getAttribute('data-column-id') === selectedColumnId - ); + // find and hide previous column's borders (which create the left edge of selected column) + const allHeaders = Array.from( + spreadsheetContainer.querySelectorAll( + '[role="rowheader"] [role="cell"][data-column-id]' + ) + ); + const selectedIndex = allHeaders.findIndex( + (cell) => cell.getAttribute('data-column-id') === selectedColumnId + ); - if (selectedIndex > 0) { - const prevColumnId = allHeaders[selectedIndex - 1].getAttribute('data-column-id'); - if (prevColumnId) { - const previousCells = spreadsheetContainer.querySelectorAll( - `[role="cell"][data-column-id="${prevColumnId}"]` - ); - previousCells.forEach((cell) => cell.classList.add('hide-border')); + if (selectedIndex > 0) { + const prevColumnId = allHeaders[selectedIndex - 1].getAttribute('data-column-id'); + if (prevColumnId) { + const previousCells = spreadsheetContainer.querySelectorAll( + `[role="cell"][data-column-id="${prevColumnId}"]` + ); + previousCells.forEach((cell) => cell.classList.add('hide-border')); + } } - } + }, 300); // update position updateColumnHighlight(); @@ -1109,6 +1118,19 @@ {/if} {/snippet} + + {#snippet mobileFooterChildren(toggle)} + { + toggle(event); + deleteColumn(column.id); + }} + style="position: absolute; left: 1rem;" + >Delete + + {/snippet} {/if} {/each} @@ -1172,7 +1194,7 @@ size="xs" variant="secondary" disabled={customColumns.filter((col) => !col.isPlaceholder).length <= 1} - on:click={deleteColumn}> + on:click={() => deleteColumn(selectedColumnName)}> Delete diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte index ce4827dba..22b576b83 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte @@ -7,6 +7,7 @@ let { children, tooltipChildren, + mobileFooterChildren, toggleOnTapClick = true, onShowStateChanged = null, enabled = true, @@ -14,6 +15,7 @@ }: { children: Snippet<[toggle: (event: Event) => void]>; tooltipChildren: Snippet<[toggle: (event: Event) => void]>; + mobileFooterChildren?: Snippet<[toggle: (event: Event) => void]>; toggleOnTapClick?: boolean; onShowStateChanged?: (showing: boolean) => void; enabled?: boolean; @@ -58,6 +60,11 @@ showSheet = false; } }}> + + {#snippet footer()} + {@render mobileFooterChildren?.(() => (showSheet = false))} + {/snippet} + {@render tooltipChildren(() => (showSheet = false))} {/if} From d45599e1609d9c2c1305a700e562e53e6f94a255 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 3 Oct 2025 09:31:55 +0530 Subject: [PATCH 07/70] add: menu for inner icon as well. --- .../(suggestions)/empty.svelte | 164 ++++++++++-------- .../(suggestions)/options.svelte | 1 - 2 files changed, 88 insertions(+), 77 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 c07686a99..28cc36df7 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 @@ -574,7 +574,7 @@ function resetSelectedColumn() { selectedColumnId = null; previousColumnId = null; - selectedColumnName = null; + /*selectedColumnName = null;*/ } function isColumnVisible(columnId: string) { @@ -677,6 +677,16 @@ creatingColumns = true; const client = sdk.forProject(page.params.region, page.params.project); + const isAnyEmpty = customColumns.some((col) => !col.key); + if (isAnyEmpty) { + creatingColumns = false; + addNotification({ + type: 'warning', + message: 'Some columns have invalid keys' + }); + return; + } + try { const results = []; @@ -938,9 +948,9 @@ {:else} {@const columnObj = getColumn(column.id)} - + )?.icon} {@const columnIconColor = !columnObj?.type ? '--non-overlay-icon-color' : '--overlay-icon-color'} @@ -979,88 +989,41 @@ {column.title} - -
- { - if ( - isColumnInteractable && - !$isTabletViewport - ) { - toggle(event); - resetSelectedColumn(); - } - }}> - {#if !columnObj?.isPlaceholder} - - {/if} - -
- -
- - - {#each basicColumnOptions as option} - { - toggle(); - updateColumn(column.id, { - type: option.type, - format: - option.format || null - }); - }}> - - - {option.name} - - - {/each} - - -
-
+ {@render changeColumnTypePopover({ + id: column.id, + columnObj, + iconColor: columnIconColor, + icon: column.icon, + isColumnInteractable, + index + })} {#if !$isTabletViewport} -
+
- +
{/if} @@ -1256,6 +1219,55 @@ {/if}
+{#snippet changeColumnTypePopover({ id, columnObj, iconColor, icon, isColumnInteractable, index })} + +
+ { + if (isColumnInteractable && !$isTabletViewport) { + toggle(event); + resetSelectedColumn(); + } + }}> + {#if !columnObj?.isPlaceholder} + + {/if} + +
+ +
+ + + {#each basicColumnOptions as option} + { + toggle(); + updateColumn(id, { + type: option.type, + format: option.format || null + }); + }}> + + + {option.name} + + + {/each} + + +
+
+{/snippet} + From 05d86898013b9b5fb9c35044ee2ae916aa3895c9 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 6 Oct 2025 16:27:52 +0530 Subject: [PATCH 10/70] update: widths. --- .../table-[table]/indexes/+page.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index ffdb00d27..08c138cf8 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -65,14 +65,14 @@ const spreadsheetColumns = $derived([ { id: 'key', - width: getColumnWidth('key', $isSmallViewport ? 250 : 200), - minimumWidth: $isSmallViewport ? 250 : 200, + width: getColumnWidth('key', 250), + minimumWidth: 250, resizable: true }, { id: 'type', - width: getColumnWidth('type', 120), - minimumWidth: 120, + width: getColumnWidth('type', 200), + minimumWidth: 200, resizable: true }, { From 9ec11dc9bb3f9da1374c141b8daa93ff8d3b0010 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 6 Oct 2025 17:17:51 +0530 Subject: [PATCH 11/70] update: columns ai suggestions [wip]. --- src/lib/elements/forms/inputLine.svelte | 11 +++- src/lib/elements/forms/inputPoint.svelte | 7 ++- src/lib/elements/forms/inputPolygon.svelte | 5 +- .../table-[table]/columns/boolean.svelte | 10 ++- .../table-[table]/columns/datetime.svelte | 14 +++-- .../table-[table]/columns/email.svelte | 14 +++-- .../table-[table]/columns/enum.svelte | 17 ++--- .../table-[table]/columns/float.svelte | 18 ++++-- .../table-[table]/columns/integer.svelte | 21 ++++--- .../table-[table]/columns/ip.svelte | 9 ++- .../table-[table]/columns/line.svelte | 15 ++++- .../table-[table]/columns/point.svelte | 15 ++++- .../table-[table]/columns/polygon.svelte | 15 ++++- .../table-[table]/columns/relationship.svelte | 14 +++-- .../table-[table]/columns/string.svelte | 18 +++--- .../table-[table]/columns/url.svelte | 18 +++--- .../table-[table]/createColumn.svelte | 63 ++++++++++++++++++- 17 files changed, 209 insertions(+), 75 deletions(-) diff --git a/src/lib/elements/forms/inputLine.svelte b/src/lib/elements/forms/inputLine.svelte index 3987b3635..6bfeb0fe2 100644 --- a/src/lib/elements/forms/inputLine.svelte +++ b/src/lib/elements/forms/inputLine.svelte @@ -14,6 +14,7 @@ onDeletePoint: (index: number) => void; onChangePoint: (pointIndex: number, coordIndex: number, newValue: number) => void; addLineButton?: Snippet; + disabled?: boolean; }; let { @@ -24,7 +25,8 @@ onAddPoint, onDeletePoint, onChangePoint, - addLineButton + addLineButton, + disabled }: Props = $props(); function isDeleteDisabled(index: number) { @@ -40,6 +42,7 @@ {#each values as value, index} - {@render addLineButton?.()} diff --git a/src/lib/elements/forms/inputPoint.svelte b/src/lib/elements/forms/inputPoint.svelte index 4c0b4ce40..9a3f70183 100644 --- a/src/lib/elements/forms/inputPoint.svelte +++ b/src/lib/elements/forms/inputPoint.svelte @@ -10,6 +10,7 @@ deletePoints?: boolean; onDeletePoint?: () => void; disableDelete?: boolean; + disabled?: boolean; onChangePoint: (index: number, newValue: number) => void; } @@ -21,7 +22,8 @@ deletePoints = false, disableDelete = false, onDeletePoint, - onChangePoint + onChangePoint, + disabled }: Props = $props(); @@ -38,6 +40,7 @@ placeholder="Enter value" step={0.0001} value={values[index]} + {disabled} on:change={(e) => onChangePoint(index, Number.parseFloat(`${e.detail}`))} /> {/each} {/if} @@ -45,7 +48,7 @@ diff --git a/src/lib/elements/forms/inputPolygon.svelte b/src/lib/elements/forms/inputPolygon.svelte index c2bcbb4db..766dfa7d1 100644 --- a/src/lib/elements/forms/inputPolygon.svelte +++ b/src/lib/elements/forms/inputPolygon.svelte @@ -17,6 +17,7 @@ coordIndex: number, newValue: number ) => void; + disabled?: boolean; }; let { @@ -26,7 +27,8 @@ onAddPoint, onAddLine, onDeletePoint, - onChangePoint + onChangePoint, + disabled }: Props = $props(); @@ -34,6 +36,7 @@ {#each values as value, index} onAddPoint(index)} {nullable} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/boolean.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/boolean.svelte index cd09096a4..83e919630 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/boolean.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/boolean.svelte @@ -39,6 +39,7 @@ import { InputSelect } from '$lib/elements/forms'; export let editing = false; + export let disabled = false; export let data: Partial = { required: false, array: false, @@ -67,6 +68,7 @@ array: false, ...data }); + $: listen(data); $: handleDefaultState($required || $array); @@ -76,24 +78,26 @@ id="default" label="Default value" placeholder="Select a value" - disabled={data.required || data.array} + disabled={data.required || data.array || disabled} options={[ { label: 'NULL', value: null }, { label: 'True', value: true }, { label: 'False', value: false } ]} bind:value={data.default} /> + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/datetime.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/datetime.svelte index 827d77798..5ef5afca1 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/datetime.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/datetime.svelte @@ -42,13 +42,13 @@ + {#if isSuggestionsFeatureEnabled} + + {/if} + @@ -195,6 +248,7 @@ id="type" label="Type" bind:value={selectedOption} + disabled={isSuggestionsEnabled} options={columnOptions.map((attr) => { return { label: attr.name, @@ -206,6 +260,9 @@ {#if selectedOption} - ($option = null)} /> + ($option = null)} /> {/if} From bd26eeaab14c7922eb109777a9a8b355ef08c689 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 8 Oct 2025 10:22:53 +0530 Subject: [PATCH 12/70] update: smoother slides. --- .../database-[database]/(suggestions)/empty.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 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 04923f1ec..a7a39faf9 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 @@ -1133,7 +1133,7 @@
{#each Array.from({ length: emptyCells }) as _} - + {#each spreadsheetColumns as column} {@const columnObj = getColumn(column.id)} {@const isColumnInteractable = @@ -1365,8 +1365,8 @@ &.slide { transition: - left 0.3s ease-out, - width 0.3s ease-out; + left 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), + width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } } From d492a0ae447d335828990cf50189aa2bf4d383d1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 8 Oct 2025 13:16:26 +0530 Subject: [PATCH 13/70] new: empty states. --- src/lib/components/card.svelte | 22 +++- .../(suggestions)/empty.svelte | 4 +- .../table-[table]/+page.svelte | 104 ++++++++++++------ .../table-[table]/indexes/+page.svelte | 66 ++++++----- .../table-[table]/layout/emptySheet.svelte | 83 ++++---------- .../layout/emptySheetCards.svelte | 48 ++++++++ 6 files changed, 195 insertions(+), 132 deletions(-) create mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte diff --git a/src/lib/components/card.svelte b/src/lib/components/card.svelte index fd94436ab..c21c90ef5 100644 --- a/src/lib/components/card.svelte +++ b/src/lib/components/card.svelte @@ -20,21 +20,23 @@ }; type ButtonProps = { - isButton: true; + isButton: boolean; href?: never; }; type AnchorProps = { href: string; - isButton?: never; + isButton?: boolean; + external?: boolean; }; + let classes = ''; type $$Props = BaseProps & (ButtonProps | AnchorProps | BaseProps) & BaseCardProps; - export let isDashed = false; - export let isButton = false; + export let isDashed: boolean = false; + export let isButton: boolean = false; export let href: string = null; - let classes = ''; + export let external: boolean = false; export { classes as class }; export let style = ''; export let padding: $$Props['padding'] = 'm'; @@ -45,7 +47,15 @@ {#if href} - + 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 a7a39faf9..aeeb58ead 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 @@ -1365,8 +1365,8 @@ &.slide { transition: - left 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), - width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + left 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), + width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } } 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 df77376fa..45e3d7f3f 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 @@ -26,13 +26,21 @@ import { addNotification } from '$lib/stores/notifications'; import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics'; import { isSmallViewport } from '$lib/stores/viewport'; - import { IconChevronDown, IconChevronUp, IconPlus } from '@appwrite.io/pink-icons-svelte'; + import { + IconBookOpen, + IconChevronDown, + IconChevronUp, + IconPlus, + IconViewBoards + } from '@appwrite.io/pink-icons-svelte'; import type { Models } from '@appwrite.io/console'; import EmptySheet from './layout/emptySheet.svelte'; import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; import { isCloud } from '$lib/system'; import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)'; + import EmptySheetCards from './layout/emptySheetCards.svelte'; + import IconAI from '../(suggestions)/icon/aiForButton.svelte'; export let data: PageData; @@ -201,58 +209,82 @@ { + customColumns={createTableColumns($table.columns, selected)}> + {#snippet actions()} + + {/snippet} + {:else} { + customColumns={createTableColumns($table.columns, selected)}> + {#snippet actions()} + { $showRowCreateSheet.show = true; - } - }, - random: { - onClick: () => { + }} /> + + { $randomDataModalState.show = true; - } - } - }} /> + }} /> + {/snippet} + {/if} {:else if isCloud && canShowSuggestionsSheet} {:else} - { + + {#snippet actions()} + { $showCreateColumnSheet.show = true; - } - }, - random: { - onClick: () => { + }} /> + + { + // todo: add a modal and show input, no toggle. + }} /> + + { $randomDataModalState.show = true; - } - } - }} /> + }} /> + + + {/snippet} + {/if}
{/key} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index 08c138cf8..2db12152f 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -39,6 +39,7 @@ import { page } from '$app/state'; import { showIndexesSuggestions } from '../../(suggestions)'; import IconAI from '../../(suggestions)/icon/aiForButton.svelte'; + import EmptySheetCards from '$routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte'; let { data @@ -289,22 +290,7 @@ {:else} - showIndexesSuggestions.update(() => true), - disabled: !$table?.columns?.length - }, - secondary: { - icon: IconPlus, - text: 'Create index', - onClick: () => (showCreateIndex = true), - disabled: !$table?.columns?.length - } - }}> + {#snippet subtitle()} Create indexes to improve query and sorting performance. Learn more in the @@ -315,20 +301,48 @@ {/snippet} + + {#snippet actions()} + { + showIndexesSuggestions.update(() => true); + }} /> + + { + showIndexesSuggestions.update(() => true); + }} /> + {/snippet} {/if} {:else} - { + + {#snippet actions()} + { $showCreateColumnSheet.show = true; - } - } - }} /> + }} /> + + { + // TODO: add a modal and show input, no toggle. + }} /> + {/snippet} + {/if} {#if selectedIndexes.length > 0} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte index 4ed7b1036..bba48ed0c 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheet.svelte @@ -19,36 +19,26 @@ expandTabs } from '../store'; import SpreadsheetContainer from './spreadsheet.svelte'; - import { type ComponentType, onDestroy, onMount, type Snippet } from 'svelte'; + import { onDestroy, onMount, type Snippet } from 'svelte'; import { debounce } from '$lib/helpers/debounce'; import { columnOptions } from '../columns/store'; type Mode = 'rows' | 'rows-filtered' | 'indexes'; - interface Action { - text?: string; - disabled?: boolean; - onClick?: () => void; - icon?: ComponentType; - } - const { mode, - showActions = true, customColumns = [], title, subtitle, - actions + actions, + showActions } = $props<{ mode: Mode; - showActions?: boolean; customColumns?: Column[]; title?: string; subtitle?: Snippet; - actions?: { - primary?: Action; - secondary?: Action; - }; + actions?: Snippet; + showActions?: boolean; }>(); let spreadsheetContainer: HTMLElement; @@ -250,7 +240,7 @@ return columns; }; - const spreadsheetColumns = $derived(mode === 'rows' ? getRowColumns() : getIndexesColumns()); + const spreadsheetColumns = $derived(mode === 'indexes' ? getIndexesColumns() : getRowColumns()); const emptyCells = $derived( ($isSmallViewport ? 14 : $isTabletViewport ? 17 : 24) + (!$expandTabs ? 2 : 0) @@ -344,58 +334,22 @@ gap="xl" alignItems="center" alignContent="center" - style="max-width: 353px"> + style="width: 653px; max-width: {$isSmallViewport ? '353px' : undefined}"> {title ?? `You have no ${mode} yet`} {@render subtitle?.()} - {#if showActions} - - {#if mode !== 'rows-filtered'} - - {@const icon = actions?.primary?.icon ?? IconPlus} - - - - {actions?.primary?.text ?? `Create ${mode}`} - - - {#if mode === 'rows' || mode === 'indexes'} - - {#if actions?.secondary?.icon} - - {/if} - - {actions?.secondary?.text ?? `Generate sample data`} - - {/if} + {#if showActions && actions} + {@const inline = mode === 'rows-filtered'} + + {#if inline} + {@render actions?.()} {:else} - - {#if actions?.primary?.icon} - - {/if} - - {actions?.primary?.text} - + + {@render actions?.()} + {/if} {/if} @@ -490,7 +444,12 @@ } .empty-actions { - margin-bottom: 8%; + margin-bottom: 10%; pointer-events: auto; + + @media (max-width: 1024px) { + // experiment + margin-bottom: 15%; + } } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte new file mode 100644 index 000000000..f38edeb62 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte @@ -0,0 +1,48 @@ + + + onClick?.()}> + + {#if icon} + + {/if} + + + + {title} + + {#if subtitle} + + {subtitle} + + {/if} + + + From 00b15cd5b072b73a3b2f485e96f3d41851990b72 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 15:21:01 +0530 Subject: [PATCH 14/70] update: design comments. --- .../databases/database-[database]/(suggestions)/empty.svelte | 4 ++-- .../database-[database]/table-[table]/createColumn.svelte | 2 +- .../database-[database]/table-[table]/indexes/+page.svelte | 2 +- 3 files changed, 4 insertions(+), 4 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 aeeb58ead..40c8e53ce 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 @@ -1543,7 +1543,7 @@ background: linear-gradient( 180deg, rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.86) 32.25%, + rgba(255, 255, 255, 0.86) 85%, /* show more of the bottom area */ #fff 100% ); z-index: 20; /* under overlay */ @@ -1558,7 +1558,7 @@ background: linear-gradient( 180deg, rgba(29, 29, 33, 0) 0%, - rgba(29, 29, 33, 0.86) 21%, + rgba(29, 29, 33, 0.86) 85%, /* show more of the bottom area */ #1d1d21 100% ); } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte index 5d6e3ee55..9b54eeb0a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte @@ -38,7 +38,7 @@ const databaseId = page.params.database; // flow isn't complete yet! - const isSuggestionsFeatureEnabled = false; + const isSuggestionsFeatureEnabled = true; let key: string = $state(column?.key ?? null); let data: Partial = $state({ diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index 2db12152f..a425ed44a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -293,7 +293,7 @@ {#snippet subtitle()} - Create indexes to improve query and sorting performance. Learn more in the + Need a hand? Learn more in the From 2f6346f931b485cd71beb7b0c277b4d1206698a8 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 16:57:09 +0530 Subject: [PATCH 15/70] feat: undo delete. --- .../(suggestions)/empty.svelte | 143 ++++++++++++++---- 1 file changed, 116 insertions(+), 27 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 40c8e53ce..3b0178eac 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 @@ -61,11 +61,18 @@ let creatingColumns = $state(false); let selectedColumnId = $state(null); - let selectedColumnName = $state(null); let previousColumnId = $state(null); + let selectedColumnName = $state(null); + + // for deleting a column + undo + let undoTimer: ReturnType | null = $state(null); + let columnBeingDeleted: (SuggestedColumnSchema & { deletedIndex?: number }) | null = + $state(null); + const baseColProps = { 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) => @@ -662,12 +669,37 @@ function deleteColumn(columnId: string) { if (!columnId) return; - // remove the selected column from customColumns - const columnIndex = customColumns.findIndex((col) => col.key === columnId); - if (columnIndex !== -1) { - customColumns = customColumns.filter((_, index) => index !== columnIndex); + let columnIndex = -1; + let columnSchema: SuggestedColumnSchema; + + for (let index = 0; index < customColumns.length; index++) { + if (customColumns[index].key === columnId) { + columnIndex = index; + columnSchema = customColumns[index]; + break; + } } + if (columnIndex !== -1) { + customColumns.splice(columnIndex, 1); + } + + // store column with its index for undo + columnBeingDeleted = { ...columnSchema, deletedIndex: columnIndex }; + + // clear any existing timer + if (undoTimer) { + clearTimeout(undoTimer); + } + + // start 10-second undo timer + undoTimer = setTimeout(() => { + undoTimer = null; + selectedColumnId = null; + columnBeingDeleted = null; + selectedColumnName = null; + }, COLUMN_DELETION_UNDO_TIMER_LIMIT); + // reset selection! resetSelectedColumn(); @@ -678,6 +710,39 @@ requestAnimationFrame(() => recalcAll()); } + function undoDelete() { + if (!columnBeingDeleted) return; + + const { deletedIndex, ...columnData } = columnBeingDeleted; + + // restore column at its original index + if (deletedIndex !== undefined && deletedIndex >= 0) { + customColumns.splice(deletedIndex, 0, columnData); + } else { + // fallback: add at the end if index is missing + customColumns.push(columnData); + } + + // clear undo state + columnBeingDeleted = null; + + // clear timer + if (undoTimer) { + clearTimeout(undoTimer); + undoTimer = null; + } + + // recalculate view after restore + requestAnimationFrame(() => { + recalcAll(); + + tick().then(() => { + selectedColumnId = columnData.key; + selectedColumnName = columnData.key; + }) + }); + } + function showIndexSuggestionsNotification() { // safeguard anyways! if (!isCloud) return; @@ -864,11 +929,9 @@ } else { selectedColumnId = columnId; } - } - $effect(() => { - console.log('selectedColumnId changed:', selectedColumnId); - }); + columnBeingDeleted = null; + } $effect(() => { if (!spreadsheetContainer) return; @@ -1185,33 +1248,59 @@ {:else if customColumns.some((col) => !col.isPlaceholder) && showFloatingBar} - -
+ + {@const isUndoDeleteMode = columnBeingDeleted && columnBeingDeleted?.key !== null} + {@const columnName = isUndoDeleteMode ? columnBeingDeleted?.key : selectedColumnName} + + {@const hasSelection = selectedColumnId !== null || isUndoDeleteMode} +
- - column selected + + + {#if isUndoDeleteMode} + was deleted. You can undo this action. + {:else} + column selected + {/if} - (selectedColumnId = null)}> - Cancel - + {#if !isUndoDeleteMode} + (selectedColumnId = null)}> + Cancel + + {/if} !col.isPlaceholder).length <= 1} - on:click={() => deleteColumn(selectedColumnName)}> - Delete + disabled={!isUndoDeleteMode && + customColumns.filter((col) => !col.isPlaceholder).length <= 1} + on:click={() => { + if (isUndoDeleteMode) { + undoDelete(); + } else { + deleteColumn(selectedColumnName); + } + }}> + {#if isUndoDeleteMode} + Undo + {:else} + Delete + {/if} @@ -1223,7 +1312,7 @@ class="floating-action-wrapper" class:expanded={!creatingColumns} class:creating-columns={creatingColumns} - class:has-selection={selectedColumnId !== null}> + class:has-selection={hasSelection}> @@ -1543,8 +1632,8 @@ background: linear-gradient( 180deg, rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.86) 85%, /* show more of the bottom area */ - #fff 100% + rgba(255, 255, 255, 0.86) 85%, + /* show more of the bottom area */ #fff 100% ); z-index: 20; /* under overlay */ display: flex; @@ -1558,8 +1647,8 @@ background: linear-gradient( 180deg, rgba(29, 29, 33, 0) 0%, - rgba(29, 29, 33, 0.86) 85%, /* show more of the bottom area */ - #1d1d21 100% + rgba(29, 29, 33, 0.86) 85%, + /* show more of the bottom area */ #1d1d21 100% ); } From f3457e67317e23180a98c894baa11b87aeda5bc7 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 17:00:36 +0530 Subject: [PATCH 16/70] fix: expanded fab shown when creating columns. --- .../(suggestions)/empty.svelte | 100 +++++++++--------- 1 file changed, 51 insertions(+), 49 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 3b0178eac..4d65ea8c6 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 @@ -1251,61 +1251,63 @@ {@const isUndoDeleteMode = columnBeingDeleted && columnBeingDeleted?.key !== null} {@const columnName = isUndoDeleteMode ? columnBeingDeleted?.key : selectedColumnName} - {@const hasSelection = selectedColumnId !== null || isUndoDeleteMode} -
- - - - - {#if isUndoDeleteMode} - was deleted. You can undo this action. - {:else} - column selected - {/if} - - + {#if !creatingColumns} +
+ + + + - - - {#if !isUndoDeleteMode} + {#if isUndoDeleteMode} + was deleted. You can undo this action. + {:else} + column selected + {/if} + + + + + + {#if !isUndoDeleteMode} + (selectedColumnId = null)}> + Cancel + + {/if} (selectedColumnId = null)}> - Cancel + variant="secondary" + disabled={!isUndoDeleteMode && + customColumns.filter((col) => !col.isPlaceholder).length <= 1} + on:click={() => { + if (isUndoDeleteMode) { + undoDelete(); + } else { + deleteColumn(selectedColumnName); + } + }}> + {#if isUndoDeleteMode} + Undo + {:else} + Delete + {/if} - {/if} - !col.isPlaceholder).length <= 1} - on:click={() => { - if (isUndoDeleteMode) { - undoDelete(); - } else { - deleteColumn(selectedColumnName); - } - }}> - {#if isUndoDeleteMode} - Undo - {:else} - Delete - {/if} - - - - -
+ + +
+
+ {/if}
Date: Sun, 19 Oct 2025 19:05:07 +0530 Subject: [PATCH 17/70] feat: column suggestions modal. --- package.json | 4 +- pnpm-lock.yaml | 20 ++-- .../(suggestions)/columns.svelte | 62 +++++++++++++ .../(suggestions)/empty.svelte | 14 ++- .../(suggestions)/input.svelte | 12 ++- .../(suggestions)/store.ts | 3 + .../table-[table]/+layout.svelte | 9 +- .../table-[table]/+page.svelte | 12 ++- .../table-[table]/createColumn.svelte | 93 ++++++++----------- .../table-[table]/indexes/+page.svelte | 6 +- 10 files changed, 156 insertions(+), 79 deletions(-) create mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte diff --git a/package.json b/package.json index fa064b99f..1cf4a182a 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,9 @@ "@ai-sdk/svelte": "^1.1.24", "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39", "@appwrite.io/pink-icons": "0.25.0", - "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996", + "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0", "@appwrite.io/pink-legacy": "^1.0.3", - "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996", + "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0", "@faker-js/faker": "^9.9.0", "@popperjs/core": "^2.11.8", "@sentry/sveltekit": "^8.38.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75e232a56..38cad3b40 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,14 +18,14 @@ importers: specifier: 0.25.0 version: 0.25.0 '@appwrite.io/pink-icons-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996 - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996(svelte@5.25.3) + specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0 + version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0(svelte@5.25.3) '@appwrite.io/pink-legacy': specifier: ^1.0.3 version: 1.0.3 '@appwrite.io/pink-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996 - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996(svelte@5.25.3) + specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0 + version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0(svelte@5.25.3) '@faker-js/faker': specifier: ^9.9.0 version: 9.9.0 @@ -269,8 +269,8 @@ packages: peerDependencies: svelte: ^4.0.0 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996} + '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0': + resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0} version: 2.0.0-RC.1 peerDependencies: svelte: ^4.0.0 @@ -284,8 +284,8 @@ packages: '@appwrite.io/pink-legacy@1.0.3': resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==} - '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996} + '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0': + resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0} version: 2.0.0-RC.2 peerDependencies: svelte: ^4.0.0 @@ -3709,7 +3709,7 @@ snapshots: dependencies: svelte: 5.25.3 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996(svelte@5.25.3)': + '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0(svelte@5.25.3)': dependencies: svelte: 5.25.3 @@ -3722,7 +3722,7 @@ snapshots: '@appwrite.io/pink-icons': 1.0.0 the-new-css-reset: 1.11.3 - '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996(svelte@5.25.3)': + '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0(svelte@5.25.3)': dependencies: '@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3) '@floating-ui/dom': 1.6.13 diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte new file mode 100644 index 000000000..9cbea3a2a --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte @@ -0,0 +1,62 @@ + + + + + + + + + + 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 4d65ea8c6..8e9fa2dfe 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 @@ -43,6 +43,12 @@ import IconAINotification from './icon/aiNotification.svelte'; + let { + userColumns = [] + }: { + userColumns?: Column[]; + } = $props(); + let resizeObserver: ResizeObserver; let spreadsheetContainer: HTMLElement; @@ -394,6 +400,7 @@ ...baseColProps }, ...finalCustomColumns, + /*...userColumns,*/ { id: 'actions', title: '', @@ -409,6 +416,8 @@ const emptyCells = $derived(($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0)); onMount(async () => { + userColumns; /* silences lint check, variable not read */ + if (spreadsheetContainer) { resizeObserver = new ResizeObserver(recalcAll); resizeObserver.observe(spreadsheetContainer); @@ -430,6 +439,7 @@ $tableColumnSuggestions.enabled = false; } + $tableColumnSuggestions.force = undefined; $tableColumnSuggestions.context = null; $tableColumnSuggestions.thinking = false; @@ -739,7 +749,7 @@ tick().then(() => { selectedColumnId = columnData.key; selectedColumnName = columnData.key; - }) + }); }); } @@ -888,6 +898,8 @@ timeout: NOTIFICATION_AND_MOCK_DELAY }); + resetSuggestionsStore(true); + // show index notification! showIndexSuggestionsNotification(); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte index 31272972c..f389a56c6 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte @@ -7,6 +7,12 @@ import { Button, InputTextarea } from '$lib/elements/forms'; import { Card, Layout, Selector, Typography } from '@appwrite.io/pink-svelte'; + const { + isModal = false + }: { + isModal?: boolean; + } = $props(); + onMount(() => { if (featureActive) { $tableColumnSuggestions.enabled = true; @@ -23,7 +29,9 @@ const subtitle = $derived.by(() => { return featureActive - ? 'Enable AI to suggest useful columns based on your table name' + ? isModal + ? 'Use AI to suggest useful columns' + : 'Enable AI to suggest useful columns based on your table name' : 'Sign up for Cloud to generate columns based on your table name'; }); @@ -42,7 +50,7 @@ - {#if featureActive} + {#if featureActive && !isModal}
({ export const showIndexesSuggestions = writable(false); +export const showColumnsSuggestionsModal = writable(false); + export const mockSuggestions: { total: number; columns: ColumnInput[] } = { total: 7, columns: [ diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte index 19aa513a5..a341b5ac4 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte @@ -65,7 +65,12 @@ import { Submit, trackEvent } from '$lib/actions/analytics'; import IndexesSuggestions from '../(suggestions)/indexes.svelte'; - import { showIndexesSuggestions, tableColumnSuggestions } from '../(suggestions)'; + import ColumnsSuggestions from '../(suggestions)/columns.svelte'; + import { + showColumnsSuggestionsModal, + showIndexesSuggestions, + tableColumnSuggestions + } from '../(suggestions)'; let editRow: EditRow; let editRelatedRow: EditRelatedRow; @@ -482,4 +487,6 @@ + + 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 45e3d7f3f..e86e54009 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 @@ -38,7 +38,11 @@ import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; import { isCloud } from '$lib/system'; - import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)'; + import { + Empty as SuggestionsEmptySheet, + tableColumnSuggestions, + showColumnsSuggestionsModal + } from '../(suggestions)'; import EmptySheetCards from './layout/emptySheetCards.svelte'; import IconAI from '../(suggestions)/icon/aiForButton.svelte'; @@ -201,7 +205,7 @@
- {#if hasColumns && hasValidColumns} + {#if hasColumns && hasValidColumns && $tableColumnSuggestions.force === false} {#if data.rows.total} @@ -250,7 +254,7 @@ {/if} {:else if isCloud && canShowSuggestionsSheet} - + {:else} {#snippet actions()} @@ -267,7 +271,7 @@ title="Suggest columns" subtitle="Use AI to generate columns" onClick={() => { - // todo: add a modal and show input, no toggle. + $showColumnsSuggestionsModal = true; }} /> import { page } from '$app/state'; - import { type Columns, type ColumnDirection } from './store'; - import { goto, invalidate } from '$app/navigation'; + import { invalidate } from '$app/navigation'; import { Dependencies } from '$lib/constants'; - import { Layout } from '@appwrite.io/pink-svelte'; + import { Alert, Layout, Link } from '@appwrite.io/pink-svelte'; import { InputSelect, InputText } from '$lib/elements/forms'; import { addNotification } from '$lib/stores/notifications'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; @@ -11,8 +10,10 @@ import type { Column } from '$lib/helpers/types'; import { preferences } from '$lib/stores/preferences'; import { onMount } from 'svelte'; - import { resolve } from '$app/paths'; - import { Input as SuggestionsInput, tableColumnSuggestions } from '../(suggestions)/index'; + + import { showColumnsSuggestionsModal } from '../(suggestions)/store'; + import IconAINotification from '../(suggestions)/icon/aiNotification.svelte'; + import { type Columns, type ColumnDirection, showCreateColumnSheet } from './store'; let { direction = null, @@ -52,16 +53,6 @@ columnOptions.find((option) => option.name === selectedOption).component ); - function initSuggestionsStore() { - if (!isSuggestionsFeatureEnabled) return; - - $tableColumnSuggestions.table = null; - $tableColumnSuggestions.enabled = false; - - $tableColumnSuggestions.context = null; - $tableColumnSuggestions.thinking = false; - } - function init() { key = null; $option = null; @@ -74,9 +65,6 @@ /* default to string */ selectedOption = 'String'; $option = columnOptions[0]; - - /* init suggestions */ - initSuggestionsStore(); } function insertColumnInOrder() { @@ -155,33 +143,6 @@ } export async function submit() { - if (isSuggestionsEnabled) { - const tableInUse = page.data.table; - // TODO: confirm what flow to use! - tableColumnSuggestions.update((store) => ({ - ...store, - thinking: true, - table: { - id: tableInUse.$id, - name: tableInUse.name - } - })); - - const { region, project, database, table } = page.params; - await goto( - resolve( - '/(console)/project-[region]-[project]/databases/database-[database]/table-[table]', - { - region, - project, - database, - table - } - ) - ); - return true; - } - try { await $option.create(databaseId, tableId, key, data); @@ -222,15 +183,23 @@ $option = columnOptions.find((option) => option.name === selectedOption); } }); - - const isSuggestionsEnabled = $derived( - isSuggestionsFeatureEnabled && $tableColumnSuggestions.enabled - ); {#if isSuggestionsFeatureEnabled} - +
+ + + + + + Need help? Let AI { + $showCreateColumnSheet.show = false; + $showColumnsSuggestionsModal = true; + }}>suggest columns based on your data + +
{/if} @@ -240,7 +209,7 @@ placeholder="Enter key" bind:value={key} autofocus - disabled={selectedOption === 'Relationship' || isSuggestionsEnabled} + disabled={selectedOption === 'Relationship'} required pattern="^[A-Za-z0-9][A-Za-z0-9._\-]*$" /> @@ -248,7 +217,6 @@ id="type" label="Type" bind:value={selectedOption} - disabled={isSuggestionsEnabled} options={columnOptions.map((attr) => { return { label: attr.name, @@ -260,9 +228,22 @@ {#if selectedOption} - ($option = null)} /> + ($option = null)} /> {/if}
+ + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index a425ed44a..665b016d9 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -37,9 +37,9 @@ import { showCreateColumnSheet } from '../store'; import { isSmallViewport } from '$lib/stores/viewport'; import { page } from '$app/state'; - import { showIndexesSuggestions } from '../../(suggestions)'; + import { showIndexesSuggestions, showColumnsSuggestionsModal } from '../../(suggestions)'; import IconAI from '../../(suggestions)/icon/aiForButton.svelte'; - import EmptySheetCards from '$routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte'; + import EmptySheetCards from '../layout/emptySheetCards.svelte'; let { data @@ -339,7 +339,7 @@ title="Suggest columns" subtitle="Use AI to generate columns" onClick={() => { - // TODO: add a modal and show input, no toggle. + $showColumnsSuggestionsModal = true; }} /> {/snippet}
From 1c8191f5c7e9a37dd203134b73c72bb6cd759992 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 19:07:33 +0530 Subject: [PATCH 18/70] update: index suggestions empty state. --- .../table-[table]/indexes/+page.svelte | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index 665b016d9..8575711d6 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -290,7 +290,7 @@ {:else} - + {#snippet subtitle()} Need a hand? Learn more in the @@ -303,15 +303,6 @@ {/snippet} {#snippet actions()} - { - showIndexesSuggestions.update(() => true); - }} /> - { showIndexesSuggestions.update(() => true); }} /> + + { + showCreateIndex = true; + }} /> {/snippet} {/if} From 292829d59c234977d95198a82188e08e7b304fa5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 19:08:36 +0530 Subject: [PATCH 19/70] fix: permissions check on empty state options --- .../database-[database]/table-[table]/indexes/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index 8575711d6..b8752153a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -290,7 +290,7 @@ {:else} - + {#snippet subtitle()} Need a hand? Learn more in the From 09c6242bcc27eeb5247f37ce35c061d1fedd6998 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 19:20:58 +0530 Subject: [PATCH 20/70] fix: view disappearing on columns gen modal > thinking sheet. update: actions on indexes empty state. --- .../database-[database]/(suggestions)/columns.svelte | 2 +- .../databases/database-[database]/(suggestions)/empty.svelte | 2 +- .../databases/database-[database]/(suggestions)/store.ts | 5 +++-- .../databases/database-[database]/table-[table]/+page.svelte | 2 +- .../database-[database]/table-[table]/indexes/+page.svelte | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte index 9cbea3a2a..5673d8745 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/columns.svelte @@ -22,9 +22,9 @@ $tableColumnSuggestions.table = null; $tableColumnSuggestions.context = null; + $tableColumnSuggestions.force = false; $tableColumnSuggestions.enabled = false; $tableColumnSuggestions.thinking = false; - $tableColumnSuggestions.force = undefined; } async function triggerColumnSuggestions() { 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 8e9fa2dfe..9cc05b304 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 @@ -436,10 +436,10 @@ // these are referenced in // `table-[table]/+page.svelte` $tableColumnSuggestions.table = null; + $tableColumnSuggestions.force = false; $tableColumnSuggestions.enabled = false; } - $tableColumnSuggestions.force = undefined; $tableColumnSuggestions.context = null; $tableColumnSuggestions.thinking = false; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts index 88b914309..f05b5ca5d 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts @@ -3,7 +3,7 @@ import { IndexType } from '@appwrite.io/console'; import { columnOptions } from '../table-[table]/columns/store'; export type TableColumnSuggestions = { - force?: boolean; + force: boolean; enabled: boolean; thinking: boolean; context?: string | undefined; @@ -44,7 +44,8 @@ export const tableColumnSuggestions = writable({ enabled: false, context: null, thinking: false, - table: null + table: null, + force: false, }); export const showIndexesSuggestions = writable(false); 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 e86e54009..1cce612e1 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 @@ -205,7 +205,7 @@
- {#if hasColumns && hasValidColumns && $tableColumnSuggestions.force === false} + {#if hasColumns && hasValidColumns && !$tableColumnSuggestions.force} {#if data.rows.total} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index b8752153a..b8e9738bd 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -324,7 +324,7 @@ {/if} {:else} - + {#snippet actions()} Date: Sun, 19 Oct 2025 19:28:25 +0530 Subject: [PATCH 21/70] address comments from coderabbit. --- .../database-[database]/(suggestions)/empty.svelte | 9 ++++++--- .../databases/database-[database]/(suggestions)/store.ts | 2 +- .../database-[database]/table-[table]/+page.svelte | 3 ++- 3 files changed, 9 insertions(+), 5 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 9cc05b304..9adb2b2d0 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 @@ -680,7 +680,7 @@ if (!columnId) return; let columnIndex = -1; - let columnSchema: SuggestedColumnSchema; + let columnSchema: SuggestedColumnSchema = null; for (let index = 0; index < customColumns.length; index++) { if (customColumns[index].key === columnId) { @@ -690,10 +690,13 @@ } } - if (columnIndex !== -1) { - customColumns.splice(columnIndex, 1); + if (columnIndex === -1 || !columnSchema) { + return; } + // remove the column + customColumns.splice(columnIndex, 1); + // store column with its index for undo columnBeingDeleted = { ...columnSchema, deletedIndex: columnIndex }; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts index f05b5ca5d..a37d84a91 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts @@ -45,7 +45,7 @@ export const tableColumnSuggestions = writable({ context: null, thinking: false, table: null, - force: false, + force: false }); export const showIndexesSuggestions = writable(false); 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 1cce612e1..e440d2eb3 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 @@ -205,7 +205,7 @@
- {#if hasColumns && hasValidColumns && !$tableColumnSuggestions.force} + {#if hasColumns && hasValidColumns && $tableColumnSuggestions.force !== true} {#if data.rows.total} @@ -266,6 +266,7 @@ $showCreateColumnSheet.show = true; }} /> + Date: Sun, 19 Oct 2025 19:35:07 +0530 Subject: [PATCH 22/70] update: empty states on indexes for self-hosted. --- .../table-[table]/indexes/+page.svelte | 81 +++++++++++++------ 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index b8e9738bd..24467f354 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -23,6 +23,7 @@ Typography } from '@appwrite.io/pink-svelte'; import { + IconBookOpen, IconDotsHorizontal, IconEye, IconPlus, @@ -40,6 +41,7 @@ import { showIndexesSuggestions, showColumnsSuggestionsModal } from '../../(suggestions)'; import IconAI from '../../(suggestions)/icon/aiForButton.svelte'; import EmptySheetCards from '../layout/emptySheetCards.svelte'; + import { isCloud } from '$lib/system'; let { data @@ -292,25 +294,29 @@ {:else} {#snippet subtitle()} - - Need a hand? Learn more in the - - docs. - - + {#if isCloud} + + Need a hand? Learn more in the + + docs. + + + {/if} {/snippet} {#snippet actions()} - { - showIndexesSuggestions.update(() => true); - }} /> + {#if isCloud} + { + showIndexesSuggestions.update(() => true); + }} /> + {/if} { showCreateIndex = true; }} /> + + {#if !isCloud} + + {/if} {/snippet} {/if} {:else} + {#snippet subtitle()} + {#if isCloud} + + Need a hand? Learn more in the + + docs. + + + {/if} + {/snippet} + {#snippet actions()} - { - $showColumnsSuggestionsModal = true; - }} /> + {#if isCloud} + { + $showColumnsSuggestionsModal = true; + }} /> + {:else} + + {/if} {/snippet} {/if} From b2fb9dd57246d11a31090fd0b78a77a3f57f9d38 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 19:41:01 +0530 Subject: [PATCH 23/70] update: empty states on rows for self-hosted. --- .../table-[table]/+page.svelte | 47 +++++++++++++------ .../table-[table]/indexes/+page.svelte | 4 +- 2 files changed, 35 insertions(+), 16 deletions(-) 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 e440d2eb3..63089d7f3 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 @@ -6,7 +6,7 @@ import { Container } from '$lib/layout'; import { preferences } from '$lib/stores/preferences'; import { canWriteTables, canWriteRows } from '$lib/stores/roles'; - import { Icon, Layout, Divider, Tooltip } from '@appwrite.io/pink-svelte'; + import { Icon, Layout, Divider, Tooltip, Typography, Link } from '@appwrite.io/pink-svelte'; import type { PageData } from './$types'; import { table, @@ -257,6 +257,20 @@ {:else} + {#snippet subtitle()} + {#if !isCloud} + + + Need a hand? Learn more in the + + docs. + + + {/if} + {/snippet} + {#snippet actions()} - - { - $showColumnsSuggestionsModal = true; - }} /> + {#if isCloud} + + { + $showColumnsSuggestionsModal = true; + }} /> + {/if} - + {#if isCloud} + + + {/if} {/snippet} {/if} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index 24467f354..11a89554e 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -331,7 +331,7 @@ {/if} {/snippet} @@ -373,7 +373,7 @@ {/if} {/snippet} From cc795fa561d9c369fafd92a1863d56179573d3a6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 19:46:11 +0530 Subject: [PATCH 24/70] update: hide ai suggestion inline alert if not cloud, for now. --- .../database-[database]/table-[table]/createColumn.svelte | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte index 1285e3585..97f06e128 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte @@ -14,6 +14,7 @@ import { showColumnsSuggestionsModal } from '../(suggestions)/store'; import IconAINotification from '../(suggestions)/icon/aiNotification.svelte'; import { type Columns, type ColumnDirection, showCreateColumnSheet } from './store'; + import { isCloud } from '$lib/system'; let { direction = null, @@ -38,9 +39,6 @@ const tableId = page.params.table; const databaseId = page.params.database; - // flow isn't complete yet! - const isSuggestionsFeatureEnabled = true; - let key: string = $state(column?.key ?? null); let data: Partial = $state({ required: column?.required ?? false, @@ -186,7 +184,7 @@ - {#if isSuggestionsFeatureEnabled} + {#if isCloud}
From 5380a6aece3c8cd33b35114977ec26751f2ee454 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 19 Oct 2025 19:47:22 +0530 Subject: [PATCH 25/70] fix: column id for deletion. --- .../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 9adb2b2d0..e02979d30 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 @@ -1309,7 +1309,7 @@ if (isUndoDeleteMode) { undoDelete(); } else { - deleteColumn(selectedColumnName); + deleteColumn(selectedColumnId); } }}> {#if isUndoDeleteMode} From d8cd72ca0b8c0f969b6d74027613fda3eb99ec51 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 20 Oct 2025 12:31:06 +0530 Subject: [PATCH 26/70] ci: empty commit From 5eaeccf7d0e78fd0bbaf87c4746fdef09d9c497f Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 21 Oct 2025 16:04:08 +0530 Subject: [PATCH 27/70] address design review comments. --- .../(suggestions)/empty.svelte | 128 +++++++++++++++--- .../(suggestions)/input.svelte | 6 +- .../table-[table]/+page.svelte | 20 +-- .../table-[table]/createColumn.svelte | 9 +- .../table-[table]/indexes/+page.svelte | 24 ++-- .../table-[table]/layout/emptySheet.svelte | 27 ++-- 6 files changed, 163 insertions(+), 51 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 e02979d30..6072015d1 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 @@ -1012,6 +1012,38 @@ }); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- + - {#if isUndoDeleteMode} - was deleted. You can undo this action. - {:else} - column selected - {/if} - + + {#if isUndoDeleteMode} + was deleted. You can undo this action. + {:else} + column selected + {/if} + + @@ -1299,6 +1337,9 @@ on:click={() => (selectedColumnId = null)}> Cancel + + + {/if} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte index f389a56c6..70dec2963 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte @@ -70,7 +70,7 @@ {#if $tableColumnSuggestions.enabled && featureActive} -
+
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 63089d7f3..29446deae 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 @@ -245,7 +245,7 @@ { $randomDataModalState.show = true; @@ -272,14 +272,6 @@ {/snippet} {#snippet actions()} - { - $showCreateColumnSheet.show = true; - }} /> - {#if isCloud} {/if} + { + $showCreateColumnSheet.show = true; + }} /> + { $randomDataModalState.show = true; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte index 97f06e128..039b0a854 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/createColumn.svelte @@ -39,6 +39,8 @@ const tableId = page.params.table; const databaseId = page.params.database; + let showSuggestionsAlert = $state(true); + let key: string = $state(column?.key ?? null); let data: Partial = $state({ required: column?.required ?? false, @@ -184,9 +186,9 @@ - {#if isCloud} + {#if isCloud && showSuggestionsAlert}
- + (showSuggestionsAlert = false)}> @@ -233,7 +235,10 @@ From e405c3cf03caa1e037a83098eb3e8244e82f0cc5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 2 Nov 2025 18:15:18 +0530 Subject: [PATCH 60/70] 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 667f52b5f..8f34edd65 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 61/70] 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 8f34edd65..8f570c4ec 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 1c1c256f8..f066514a0 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 62/70] 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 1d51db168..9fae0e544 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 63/70] 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 8f570c4ec..a11563025 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 64/70] 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 a11563025..50a2fa15a 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 65/70] update: cache on format. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a2e77773..75f11ee7f 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 66/70] 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 50a2fa15a..c0d2bd007 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 67/70] update: animation. --- .../(suggestions)/empty.svelte | 424 ++++++++++-------- 1 file changed, 240 insertions(+), 184 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 c0d2bd007..7194bd707 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); + /* 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: 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: rgba(253, 54, 110, 0.04); - - .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; + 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; } } - /* hide glow wrapper entirely in shimmer mode */ - &[data-anim='shimmer'] { - .ai-border-glow { - display: none; + /* 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); + + /* 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; + 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; + } + + /* edge glows container */ + .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 { + animation: leftPulse 6s ease-in-out infinite; + } + + .edge-glow.right .grad { + animation: rightPulse 6s ease-in-out infinite; + } + + /* 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.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 68/70] 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 7194bd707..a4ceee4ae 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 69/70] 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 75f11ee7f..b4fc20d0f 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 a4ceee4ae..02c06bec1 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 70/70] cleanup. --- .../(suggestions)/empty.svelte | 293 +++++++----------- 1 file changed, 117 insertions(+), 176 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 02c06bec1..f7c0b189d 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); + background: 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; - } + --border-pink: rgba(253, 54, 110, 0.4); + --border-orange: rgba(254, 149, 103, 0.25); - .inner-glow-wrapper { - display: none; - } + &::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% + &::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; + } + + .edge-glow { + position: absolute; + inset: 0; + pointer-events: none; + border-radius: inherit; + } + + .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.right .grad { + animation: rightPulse 6s ease-in-out infinite backwards; + } + + .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% ); - }*/ + } - 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% + .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% ); - 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; - } - - .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 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 { - animation: leftPulse 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.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) {