Skip to content
Closed
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
1 change: 1 addition & 0 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@trpc/server": "^11.7.1",
"@trpc/tanstack-react-query": "^11.7.1",
"date-fns": "^4.1.0",
"drizzle-orm": "0.45.1",
"import-in-the-middle": "2.0.1",
"next": "^16.0.10",
"next-themes": "^0.4.6",
Expand Down
3 changes: 2 additions & 1 deletion apps/admin/src/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { clerkMiddleware } from "@clerk/nextjs/server";
import { db, eq } from "@superset/db";
import { db } from "@superset/db/client";
import { users } from "@superset/db/schema";
import { COMPANY } from "@superset/shared/constants";
import { eq } from "drizzle-orm";
import { NextResponse } from "next/server";

import { env } from "./env";
Expand Down
29 changes: 28 additions & 1 deletion apps/desktop/electron-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const config: Configuration = {
// ASAR configuration for native modules and external resources
asar: true,
asarUnpack: [
"**/node_modules/better-sqlite3/**/*",
// better-sqlite3 uses `bindings` to locate native modules - must be unpacked together
"**/node_modules/bindings/**/*",
"**/node_modules/file-uri-to-path/**/*",
"**/node_modules/node-pty/**/*",
// Sound files must be unpacked so external audio players (afplay, paplay, etc.) can access them
"**/resources/sounds/**/*",
Expand All @@ -46,9 +50,32 @@ const config: Configuration = {
to: "resources",
filter: ["**/*"],
},
// Native module that can't be bundled by Vite.
// Database migrations from local-db package (copied to dist/resources/migrations by vite)
{
from: "dist/resources/migrations",
to: "resources/migrations",
filter: ["**/*"],
},
// Native modules that can't be bundled by Vite.
// The copy:native-modules script replaces symlinks with real files
// before building (required for Bun 1.3+ isolated installs).
{
from: "node_modules/better-sqlite3",
to: "node_modules/better-sqlite3",
filter: ["**/*"],
},
// better-sqlite3 uses `bindings` package to locate its native .node file
{
from: "node_modules/bindings",
to: "node_modules/bindings",
filter: ["**/*"],
},
// `bindings` requires `file-uri-to-path` for file:// URL handling
{
from: "node_modules/file-uri-to-path",
to: "node_modules/file-uri-to-path",
filter: ["**/*"],
},
{
from: "node_modules/node-pty",
to: "node_modules/node-pty",
Expand Down
32 changes: 24 additions & 8 deletions apps/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,31 @@ function copyResourcesPlugin(): Plugin {
return {
name: "copy-resources",
writeBundle() {
const srcDir = resolve(resources, "sounds");
const destDir = resolve(devPath, "resources/sounds");
// Copy sounds
const soundsSrc = resolve(resources, "sounds");
const soundsDest = resolve(devPath, "resources/sounds");

if (existsSync(srcDir)) {
// Clean destination to avoid stale files
if (existsSync(destDir)) {
rmSync(destDir, { recursive: true });
if (existsSync(soundsSrc)) {
if (existsSync(soundsDest)) {
rmSync(soundsDest, { recursive: true });
}
mkdirSync(destDir, { recursive: true });
cpSync(srcDir, destDir, { recursive: true });
mkdirSync(soundsDest, { recursive: true });
cpSync(soundsSrc, soundsDest, { recursive: true });
}

// Copy database migrations from local-db package
const migrationsSrc = resolve(
__dirname,
"../../packages/local-db/drizzle",
);
const migrationsDest = resolve(devPath, "resources/migrations");

if (existsSync(migrationsSrc)) {
if (existsSync(migrationsDest)) {
rmSync(migrationsDest, { recursive: true });
}
mkdirSync(migrationsDest, { recursive: true });
cpSync(migrationsSrc, migrationsDest, { recursive: true });
}
},
};
Expand Down Expand Up @@ -86,6 +101,7 @@ export default defineConfig({
// Only externalize native modules that can't be bundled
external: [
"electron",
"better-sqlite3", // Native module - must stay external
"node-pty", // Native module - must stay external
],
},
Expand Down
6 changes: 5 additions & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@superset/desktop",
"productName": "Superset",
"description": "The last developer tool you'll ever need",
"version": "0.0.33",
"version": "0.0.34",
"main": "./dist/main/index.js",
"resources": "src/resources",
"repository": {
Expand Down Expand Up @@ -37,6 +37,7 @@
"@monaco-editor/react": "^4.7.0",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.8",
"@superset/local-db": "workspace:*",
"@superset/shared": "workspace:*",
"@superset/trpc": "workspace:*",
"@superset/ui": "workspace:*",
Expand All @@ -57,12 +58,14 @@
"@xterm/addon-web-links": "^0.11.0",
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"better-sqlite3": "12.5.0",
"clsx": "^2.1.1",
"culori": "^4.0.2",
"date-fns": "^4.1.0",
"default-shell": "^2.2.0",
"dnd-core": "^16.0.1",
"dotenv": "^17.2.3",
"drizzle-orm": "0.45.1",
"electron-router-dom": "^2.1.0",
"electron-updater": "6",
"execa": "^9.6.0",
Expand Down Expand Up @@ -106,6 +109,7 @@
"@biomejs/biome": "^2.3.8",
"@superset/typescript": "workspace:*",
"@tailwindcss/vite": "^4.0.9",
"@types/better-sqlite3": "^7.6.13",
"@types/culori": "^4.0.1",
"@types/http-proxy": "^1.17.17",
"@types/lodash": "^4.17.20",
Expand Down
71 changes: 48 additions & 23 deletions apps/desktop/scripts/copy-native-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,67 @@
import { cpSync, existsSync, lstatSync, realpathSync, rmSync } from "node:fs";
import { dirname, join } from "node:path";

const NATIVE_MODULES = ["node-pty"] as const;
// Native modules that must exist for the app to work
const NATIVE_MODULES = ["better-sqlite3", "node-pty"] as const;

function prepareNativeModules() {
console.log("Preparing native modules for electron-builder...");
// Dependencies of native modules that need to be copied (may be hoisted or symlinked)
const NATIVE_MODULE_DEPS = ["bindings", "file-uri-to-path"] as const;

const nodeModulesDir = join(dirname(import.meta.dirname), "node_modules");

for (const moduleName of NATIVE_MODULES) {
const modulePath = join(nodeModulesDir, moduleName);
function copyModuleIfSymlink(
nodeModulesDir: string,
moduleName: string,
required: boolean,
): boolean {
const modulePath = join(nodeModulesDir, moduleName);

if (!existsSync(modulePath)) {
if (!existsSync(modulePath)) {
if (required) {
console.error(` [ERROR] ${moduleName} not found at ${modulePath}`);
process.exit(1);
}
console.log(` ${moduleName}: not found (skipping)`);
return false;
}

const stats = lstatSync(modulePath);
const stats = lstatSync(modulePath);

if (stats.isSymbolicLink()) {
// Resolve symlink to get real path
const realPath = realpathSync(modulePath);
console.log(` ${moduleName}: symlink -> replacing with real files`);
console.log(` Real path: ${realPath}`);
if (stats.isSymbolicLink()) {
// Resolve symlink to get real path
const realPath = realpathSync(modulePath);
console.log(` ${moduleName}: symlink -> replacing with real files`);
console.log(` Real path: ${realPath}`);

// Remove the symlink
rmSync(modulePath);
// Remove the symlink
rmSync(modulePath);

// Copy the actual files
cpSync(realPath, modulePath, { recursive: true });
// Copy the actual files
cpSync(realPath, modulePath, { recursive: true });

console.log(` Copied to: ${modulePath}`);
} else {
console.log(` ${moduleName}: already real directory (not a symlink)`);
}
console.log(` Copied to: ${modulePath}`);
} else {
console.log(` ${moduleName}: already real directory (not a symlink)`);
}

return true;
}

function prepareNativeModules() {
console.log("Preparing native modules for electron-builder...");

const nodeModulesDir = join(dirname(import.meta.dirname), "node_modules");

// Copy required native modules
for (const moduleName of NATIVE_MODULES) {
copyModuleIfSymlink(nodeModulesDir, moduleName, true);
}

// Copy native module dependencies (not required but needed if present)
console.log("\nPreparing native module dependencies...");
for (const moduleName of NATIVE_MODULE_DEPS) {
copyModuleIfSymlink(nodeModulesDir, moduleName, false);
}

console.log("Done!");
console.log("\nDone!");
}

prepareNativeModules();
34 changes: 21 additions & 13 deletions apps/desktop/src/lib/trpc/routers/changes/branches.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { db } from "main/lib/db";
import { worktrees } from "@superset/local-db";
import { eq } from "drizzle-orm";
import { localDb } from "main/lib/local-db";
import simpleGit from "simple-git";
import { z } from "zod";
import { publicProcedure, router } from "../..";
Expand Down Expand Up @@ -59,24 +61,30 @@ export const createBranchesRouter = () => {
.mutation(async ({ input }): Promise<{ success: boolean }> => {
const git = simpleGit(input.worktreePath);

const worktree = db.data.worktrees.find(
(wt) => wt.path === input.worktreePath,
);
const worktree = localDb
.select()
.from(worktrees)
.where(eq(worktrees.path, input.worktreePath))
.get();
if (!worktree) {
throw new Error(`No worktree found at path "${input.worktreePath}"`);
}

await git.checkout(input.branch);

await db.update((data) => {
const wt = data.worktrees.find((w) => w.path === input.worktreePath);
if (wt) {
wt.branch = input.branch;
if (wt.gitStatus) {
wt.gitStatus.branch = input.branch;
}
}
});
// Update the branch in the worktree record
const gitStatus = worktree.gitStatus
? { ...worktree.gitStatus, branch: input.branch }
: null;

localDb
.update(worktrees)
.set({
branch: input.branch,
gitStatus,
})
.where(eq(worktrees.path, input.worktreePath))
.run();

return { success: true };
}),
Expand Down
35 changes: 24 additions & 11 deletions apps/desktop/src/lib/trpc/routers/config/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { db } from "main/lib/db";
import { projects } from "@superset/local-db";
import { eq } from "drizzle-orm";
import { localDb } from "main/lib/local-db";
import { z } from "zod";
import { publicProcedure, router } from "../..";

Expand Down Expand Up @@ -41,7 +43,11 @@ export const createConfigRouter = () => {
shouldShowConfigToast: publicProcedure
.input(z.object({ projectId: z.string() }))
.query(({ input }) => {
const project = db.data.projects.find((p) => p.id === input.projectId);
const project = localDb
.select()
.from(projects)
.where(eq(projects.id, input.projectId))
.get();
if (!project) {
return false;
}
Expand All @@ -57,21 +63,24 @@ export const createConfigRouter = () => {
// Mark the config toast as dismissed for a project
dismissConfigToast: publicProcedure
.input(z.object({ projectId: z.string() }))
.mutation(async ({ input }) => {
await db.update((data) => {
const project = data.projects.find((p) => p.id === input.projectId);
if (project) {
project.configToastDismissed = true;
}
});
.mutation(({ input }) => {
localDb
.update(projects)
.set({ configToastDismissed: true })
.where(eq(projects.id, input.projectId))
.run();
return { success: true };
}),

// Get the config file path (creates it if it doesn't exist)
getConfigFilePath: publicProcedure
.input(z.object({ projectId: z.string() }))
.query(({ input }) => {
const project = db.data.projects.find((p) => p.id === input.projectId);
const project = localDb
.select()
.from(projects)
.where(eq(projects.id, input.projectId))
.get();
if (!project) {
return null;
}
Expand All @@ -82,7 +91,11 @@ export const createConfigRouter = () => {
getConfigContent: publicProcedure
.input(z.object({ projectId: z.string() }))
.query(({ input }) => {
const project = db.data.projects.find((p) => p.id === input.projectId);
const project = localDb
.select()
.from(projects)
.where(eq(projects.id, input.projectId))
.get();
if (!project) {
return { content: null, exists: false };
}
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/lib/trpc/routers/external/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { spawn } from "node:child_process";
import nodePath from "node:path";
import { EXTERNAL_APPS, type ExternalApp } from "main/lib/db/schemas";
import { EXTERNAL_APPS, type ExternalApp } from "@superset/local-db";

/** Map of app IDs to their macOS application names */
const APP_NAMES: Record<ExternalApp, string | null> = {
Expand Down
Loading