diff --git a/src/lib/components/Editor/Editor.svelte b/src/lib/components/Editor/Editor.svelte index 6e18f97..9935e43 100644 --- a/src/lib/components/Editor/Editor.svelte +++ b/src/lib/components/Editor/Editor.svelte @@ -36,7 +36,7 @@ value = update.state.doc.toString(); } }), - placeholder('Enter a query...') + placeholder('Type your query...') ] }); diff --git a/src/lib/migrations/003_create_tabs_table.sql b/src/lib/migrations/003_create_tabs_table.sql index bb4cc10..79bb78b 100644 --- a/src/lib/migrations/003_create_tabs_table.sql +++ b/src/lib/migrations/003_create_tabs_table.sql @@ -3,5 +3,6 @@ CREATE TABLE IF NOT EXISTS tabs ( name TEXT NOT NULL, contents TEXT NOT NULL, query_id INTEGER, - tab_index INTEGER NOT NULL + tab_index INTEGER NOT NULL, + active BOOL NOT NULL DEFAULT FALSE ); diff --git a/src/lib/repositories/tabs.ts b/src/lib/repositories/tabs.ts index 41d2e52..0407b7f 100644 --- a/src/lib/repositories/tabs.ts +++ b/src/lib/repositories/tabs.ts @@ -8,27 +8,37 @@ export interface Tab { } export interface TabRepository { - get(): Promise; - set(tabs: Tab[]): Promise; + get(): Promise<[tabs: Tab[], activeIndex: number]>; + save(tabs: Tab[], activeIndex: number): Promise; } class SQLiteTabRepository implements TabRepository { constructor(private db: Database) {} - async get(): Promise { + async get(): Promise<[tabs: Tab[], activeIndex: number]> { const rows = await this.db.exec('select * from tabs order by tab_index'); - return rows.map(row_to_tab); + let index = rows.findIndex((r) => r.active); + return [rows.map(row_to_tab), Math.max(0, index)]; } - async set(tabs: Tab[]): Promise { + async save(tabs: Tab[], activeIndex: number): Promise { const rows = tabs.map((tab, tab_index) => ({ ...tab, tab_index })); await this.db.exec( `DELETE FROM tabs; -INSERT INTO tabs (id, name, contents, query_id, tab_index) -VALUES ${Array.from({ length: rows.length }).fill('(?,?,?,?,?)').join(',\n')} +INSERT INTO tabs (id, name, contents, query_id, tab_index, active) +VALUES ${Array.from({ length: rows.length }).fill('(?,?,?,?,?, ?)').join(',\n')} `, - rows.map((r) => [r.id, r.name, r.contents, r.query_id ?? null, r.tab_index]).flat() + rows + .map((r) => [ + r.id, + r.name, + r.contents, + r.query_id ?? null, + r.tab_index, + r.tab_index === activeIndex + ]) + .flat() ); } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 007756d..079cc25 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -26,7 +26,7 @@ let loading = $state(false); async function handleExec() { - const query = current_tab.contents; + const query = currentTab.contents; if (loading || !query) { return; } @@ -65,10 +65,10 @@ } function handleHistoryClick(entry: HistoryEntry) { - if (current_tab.contents) { - selected_tab_index = + if (currentTab.contents) { + selectedTabIndex = tabs.push({ id: crypto.randomUUID(), contents: entry.content, name: 'Untitled' }) - 1; - } else tabs[selected_tab_index] = { ...current_tab, contents: entry.content }; + } else tabs[selectedTabIndex] = { ...currentTab, contents: entry.content }; if (is_mobile) open_drawer = false; } @@ -90,9 +90,9 @@ async function handleCreateQuery({ name }: Parameters['onCreate']>>['0']) { - const q = await query_repository.create(name, current_tab.contents); + const q = await query_repository.create(name, currentTab.contents); queries = queries.concat(q); - tabs[selected_tab_index] = { ...current_tab, name, query_id: q.id }; + tabs[selectedTabIndex] = { ...currentTab, name, query_id: q.id }; } async function handleDeleteQuery(query: Query) { @@ -103,8 +103,8 @@ function handleQueryOpen(query: Query) { const index = tabs.findIndex((t) => t.query_id === query.id); if (index === -1) { - if (current_tab.contents) { - selected_tab_index = + if (currentTab.contents) { + selectedTabIndex = tabs.push({ id: crypto.randomUUID(), contents: query.sql, @@ -112,13 +112,13 @@ query_id: query.id }) - 1; } else - tabs[selected_tab_index] = { - ...current_tab, + tabs[selectedTabIndex] = { + ...currentTab, contents: query.sql, name: query.name, query_id: query.id }; - } else selected_tab_index = index; + } else selectedTabIndex = index; if (is_mobile) open_drawer = false; } @@ -135,7 +135,7 @@ } async function handleSaveQuery() { - const { contents, query_id } = current_tab; + const { contents, query_id: query_id } = currentTab; if (contents && !query_id) { return save_query_modal?.show(); } @@ -158,43 +158,42 @@ if (!is_mobile) open_drawer = false; }); - let tabs = $state([{ id: crypto.randomUUID(), contents: '', name: 'Untitled' }]); + let tabs = $state([]); $effect( () => - void tab_repository.get().then((t) => { - if (t.length) tabs = t; + void tab_repository.get().then(([t, active]) => { + if (t.length) (tabs = t), (selectedTabIndex = active); + else tabs.push({ id: crypto.randomUUID(), contents: '', name: 'Untitled' }); }) ); - const set_tabs = debounce((tabs: Tab[]) => tab_repository.set(tabs), 300); + const saveTabs = debounce( + (tabs: Tab[], activeIndex: number) => tab_repository.save(tabs, activeIndex), + 2_000 + ); - let selected_tab_index = $state(0); - const current_tab = $derived(tabs[selected_tab_index]); - const can_save = $derived.by(() => { - if (current_tab.query_id) { - const query = queries.find((q) => q.id === current_tab.query_id); - if (!query) return true; - return query.sql !== current_tab.contents; + let selectedTabIndex = $state(0); + const currentTab = $derived(tabs[selectedTabIndex]); + const canSave = $derived.by(() => { + if (!tabs.length) return false; + if (currentTab.query_id) { + const query = queries.find((q) => q.id === currentTab.query_id); + return query?.sql !== currentTab.contents; } - return !!current_tab.contents; + return !!currentTab.contents; }); function addNewTab() { - const next_index = tabs.length; - tabs.push({ id: crypto.randomUUID(), name: 'Untitled', contents: '' }); - selected_tab_index = next_index; + selectedTabIndex = tabs.push({ id: crypto.randomUUID(), name: 'Untitled', contents: '' }) - 1; } function closeTab(index: number) { tabs.splice(index, 1); - selected_tab_index = Math.max(0, selected_tab_index - 1); + selectedTabIndex = Math.max(0, selectedTabIndex - 1); } - $effect(() => { - $state.snapshot(tabs); - set_tabs(tabs).catch(console.error); - }); + $effect(() => void saveTabs($state.snapshot(tabs), selectedTabIndex).catch(console.error)); @@ -245,10 +244,10 @@ {#each tabs as tab, i} closeTab(i)} - onSelect={() => (selected_tab_index = i)} + onSelect={() => (selectedTabIndex = i)} /> {/each}