Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ jobs:
TARGET_SHA: ${{ steps.beta_meta.outputs.commit_sha }}
run: |
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
git tag -f "$RELEASE_TAG" "$TARGET_SHA"
git push --force origin "refs/tags/$RELEASE_TAG"
gh release edit "$RELEASE_TAG" \
--title "$RELEASE_TAG" \
--notes-file "$NOTES_FILE" \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/macos-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_context.outputs.tag != '' && needs.resolve_context.outputs.tag || github.ref }}
ref: ${{ github.event_name == 'release' && needs.resolve_context.outputs.tag || github.ref }}
fetch-depth: 0

- name: Setup Node
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:lts AS installer
FROM node:24-bookworm AS installer
WORKDIR /app

# Install bun and enable corepack for yarn
Expand Down Expand Up @@ -29,7 +29,7 @@ RUN yarn run build \
&& cp -rn node_modules/@electric-sql/pglite/dist/. apps/web/dist-scripts/ \
&& rm -f apps/web/.next/standalone/.env apps/web/.next/standalone/.env.local

FROM node:lts-slim AS runner
FROM node:24-bookworm-slim AS runner

ENV NODE_ENV=production \
HOSTNAME=0.0.0.0 \
Expand Down
22 changes: 22 additions & 0 deletions apps/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,28 @@ ipcMain.handle('auth:openExternal', async (_event, url: string) => {
await shell.openExternal(url);
});

ipcMain.handle('filesystem:select-sqlite-file', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [
{
name: 'SQLite Database',
extensions: ['sqlite', 'db', 'sqlite3'],
},
{
name: 'All Files',
extensions: ['*'],
},
],
});

if (result.canceled) {
return null;
}

return result.filePaths[0] ?? null;
});

ipcMain.on('log:renderer', (_event, level: string, ...args: unknown[]) => {
const safeArgs = args.map(arg => {
if (typeof arg === 'string') return arg;
Expand Down
12 changes: 6 additions & 6 deletions apps/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
"scripts": {
"compile": "tsc -p tsconfig.json && node ./scripts/copy-assets.cjs",
"dev": "yarn run compile && electron ./dist-electron/main.js",
"dev:app": "yarn run compile && electron-builder --config electron-builder.mjs --mac --dir && open \"dist/mac-arm64/Dory.app\"",
"dev:app": "yarn run electron:standalone && yarn run compile && electron-builder --config electron-builder.mjs --mac --dir && open \"dist/mac-arm64/Dory.app\"",
"electron:standalone": "bash ../../scripts/build-app-standalone.sh",
"build": "yarn --cwd ../web run build && yarn run compile && electron-builder --config electron-builder.mjs",
"electron:build:apple": "yarn run compile && electron-builder --config electron-builder.mjs --mac --publish never",
"electron:build:windows": "yarn run compile && electron-builder --config electron-builder.mjs --win --publish never",
"electron:build:apple:quick": "yarn run compile && SKIP_NOTARIZE=1 electron-builder --config electron-builder.mjs --mac zip",
"electron:build:apple:unsigned": "yarn run compile && CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --config electron-builder.mjs --mac --dir"
"build": "yarn run electron:standalone && yarn run compile && electron-builder --config electron-builder.mjs",
"electron:build:apple": "yarn run electron:standalone && yarn run compile && electron-builder --config electron-builder.mjs --mac --publish never",
"electron:build:windows": "yarn run electron:standalone && yarn run compile && electron-builder --config electron-builder.mjs --win --publish never",
"electron:build:apple:quick": "yarn run electron:standalone && yarn run compile && SKIP_NOTARIZE=1 electron-builder --config electron-builder.mjs --mac zip",
"electron:build:apple:unsigned": "yarn run electron:standalone && yarn run compile && CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --config electron-builder.mjs --mac --dir"
},
"devDependencies": {
"@types/node": "^24.3.1",
Expand Down
1 change: 1 addition & 0 deletions apps/electron/preload.cts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ipcRenderer.send('log:renderer', 'info', '[preload] loaded');
contextBridge.exposeInMainWorld('electron', {
platform: process.platform,
isPackaged: process.env.NODE_ENV === 'production' || process.env.ELECTRON_IS_PACKAGED === 'true',
selectSqliteFile: () => ipcRenderer.invoke('filesystem:select-sqlite-file') as Promise<string | null>,
});

contextBridge.exposeInMainWorld('authBridge', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type CopilotContextSQL = {
baseline: {
database?: string | null;
dialect?: 'clickhouse' | 'duckdb' | 'mysql' | 'postgres' | 'unknown';
dialect?: 'clickhouse' | 'duckdb' | 'mysql' | 'postgres' | 'sqlite' | 'unknown';
};

draft: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,20 @@ import { ConnectionCheckStatus, ConnectionIdentity, ConnectionListIdentity, Conn
import { currentConnectionAtom } from '@/shared/stores/app.store';
import { useConnectConnection } from '../../../connections/hooks/use-connect-connection';
import { useConnections } from '../../../connections/hooks/use-connections';
import { getConnectionLocationLabel } from '@/lib/connection/display';

function getInitial(text?: string | null) {
if (!text) return 'C';
const letter = text.trim()[0];
return letter ? letter.toUpperCase() : 'C';
}

function formatHostWithPort(connection?: ConnectionListItem['connection'] | null) {
if (!connection) return null;
const rawHost = connection.host?.trim();
const port = connection.port;
if (!rawHost && !port) return null;
if (rawHost && port) return `${rawHost}:${port}`;
if (rawHost) return rawHost;
if (typeof port === 'number') return `:${port}`;
return null;
}

function getHostLabel(
connection: ConnectionListItem['connection'] | null,
isLoading: boolean,
t: ReturnType<typeof useTranslations>,
) {
const hostWithPort = formatHostWithPort(connection);
const hostWithPort = getConnectionLocationLabel(connection);
if (hostWithPort) return hostWithPort;
return isLoading ? t('Loading connections') : t('No connections yet');
}
Expand Down Expand Up @@ -340,7 +330,7 @@ export function ConnectionSwitcher() {
const connectionLoadingKey = makeLoadingKey(connection.connection.id, currentIdentity?.id);
const connectionLoading = Boolean(connectLoadings?.[connectionLoadingKey]);

const host = formatHostWithPort(connection.connection) ?? t('Unknown host');
const host = getConnectionLocationLabel(connection.connection) ?? t('Unknown host');

return connection.identities?.length > 1 ? (
<DropdownMenuSub key={connection.connection.id}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const SIDEBAR_CONFIG_BY_DIALECT: Record<ConnectionType, SidebarConfig> = {
defaultSchemaName: 'public',
hiddenDatabases: ['system', 'information_schema'],
},
sqlite: {
dialect: 'sqlite',
supportsSchemas: false,
hiddenDatabases: [],
},
};

export function getSidebarConfig(connectionType?: ConnectionType | null): SidebarConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function DataPreview({
const [query, setQuery] = useState('');
const [rows, setRows] = useState<ResultRow[]>([]);
const [loading, setLoading] = useState(false);
const [hasLoaded, setHasLoaded] = useState(false);
const [error, setError] = useState<string | null>(null);
const [stats, setStats] = useState({ filteredCount: 0, totalCount: 0 });
const [inspectorOpen, setInspectorOpen] = useState(false);
Expand All @@ -95,6 +96,8 @@ function DataPreview({

useEffect(() => {
setRows([]);
setLoading(Boolean(connectionId && databaseName && tableName));
setHasLoaded(false);
setSessionMeta({});
setQuery('');
setStats({ filteredCount: 0, totalCount: 0 });
Expand Down Expand Up @@ -142,7 +145,10 @@ function DataPreview({
if (e?.name === 'AbortError') return;
setError(e?.message ?? t('Failed to load data preview'));
} finally {
setLoading(false);
if (!signal?.aborted) {
setLoading(false);
setHasLoaded(true);
}
}
},
[connectionId, databaseName, source, storageKey, tableName, setSessionMeta, t],
Expand Down Expand Up @@ -230,7 +236,7 @@ function DataPreview({
);
}

if (loading && rows.length === 0) {
if (!hasLoaded || (loading && rows.length === 0)) {
return (
<div className="h-full flex items-center justify-center text-sm text-muted-foreground">
{t('Loading preview')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ type TableViewTabsProps = {
onSubTabChange?: (tab: TableSubTab) => void;
};

const SUB_TABS: TableSubTab[] = ['overview', 'data', 'structure', 'stats'];

export function TableViewTabs({ connectionId, databaseName, tableName, driver, activeSubTab, initialSubTab = 'overview', onSubTabChange }: TableViewTabsProps) {
const t = useTranslations('TableBrowser');
const subTabs = useMemo<TableSubTab[]>(
() => (driver === 'sqlite' ? ['overview', 'data', 'structure'] : ['overview', 'data', 'structure', 'stats']),
[driver],
);
const [currentTab, setCurrentTab] = useState<TableSubTab>(activeSubTab ?? initialSubTab);

useEffect(() => {
Expand All @@ -32,7 +34,7 @@ export function TableViewTabs({ connectionId, databaseName, tableName, driver, a
}, [activeSubTab]);

const handleTabChange = (value: string) => {
const next = (SUB_TABS.find(tab => tab === value) ?? 'data') as TableSubTab;
const next = (subTabs.find(tab => tab === value) ?? 'data') as TableSubTab;
setCurrentTab(next);
onSubTabChange?.(next);
};
Expand All @@ -42,7 +44,7 @@ export function TableViewTabs({ connectionId, databaseName, tableName, driver, a
return (
<Tabs value={currentTab} onValueChange={handleTabChange} className="flex flex-col h-full" key={contentKey}>
<TabsList className="justify-start">
{SUB_TABS.map(tab => (
{subTabs.map(tab => (
<TabsTrigger key={tab} value={tab} className="cursor-pointer">
{t(`Tabs.${tab}`)}
</TabsTrigger>
Expand All @@ -59,9 +61,11 @@ export function TableViewTabs({ connectionId, databaseName, tableName, driver, a
<TabsContent value="structure" className="h-full">
<TableStructure databaseName={databaseName} tableName={tableName} />
</TabsContent>
<TabsContent value="stats" className="h-full">
<TableStats databaseName={databaseName} tableName={tableName} driver={driver} />
</TabsContent>
{driver !== 'sqlite' ? (
<TabsContent value="stats" className="h-full">
<TableStats databaseName={databaseName} tableName={tableName} driver={driver} />
</TabsContent>
) : null}
</div>
</Tabs>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ConnectionCheckStatus, ConnectionListItem } from '@/types/connections';
import { Edit2, Trash2, Loader2 } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useHasMounted } from '@/hooks/use-has-mounted';
import { getConnectionLocationLabel } from '@/lib/connection/display';

type Props = {
connectionItem: ConnectionListItem;
Expand All @@ -23,6 +24,7 @@ export default function ConnectionCard({ connectionItem, id, connectLoading, err
const hasMounted = useHasMounted();

const connection = connectionItem.connection;
const locationLabel = getConnectionLocationLabel(connection);
const lastCheckStatus = (connection?.lastCheckStatus ?? 'unknown') as ConnectionCheckStatus;
const lastCheckError = connection?.lastCheckError;
const lastCheckAt = connection?.lastCheckAt ? new Date(connection.lastCheckAt) : null;
Expand Down Expand Up @@ -102,10 +104,10 @@ export default function ConnectionCard({ connectionItem, id, connectLoading, err

<Tooltip>
<TooltipTrigger asChild>
<p className="min-h-6 max-w-full truncate text-sm text-muted-foreground">{connectionItem?.connection.host}</p>
<p className="min-h-6 max-w-full truncate text-sm text-muted-foreground">{locationLabel}</p>
</TooltipTrigger>
<TooltipContent>
<p className="max-w-xs break-all text-center">{connectionItem?.connection.host}</p>
<p className="max-w-xs break-all text-center">{locationLabel}</p>
</TooltipContent>
</Tooltip>

Expand Down
Loading
Loading