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
9 changes: 8 additions & 1 deletion apps/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,14 @@ export default defineConfig({
output: {
dir: resolve(devPath, "main"),
},
external: ["electron", "better-sqlite3", "node-pty", "pg-native"],
external: [
"electron",
"better-sqlite3",
"node-pty",
"pg-native",
"mastracode",
"@ast-grep/napi",
],
Comment on lines +105 to +112
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find packaging/copy configuration files ==="
fd "copy-native-modules|electron-builder" -t f

echo
echo "=== Check for copy-native-modules implementation ==="
fd "copy-native-modules" -t f -x head -100 {}

echo
echo "=== Inspect electron.vite.config.ts externals section ==="
rg -n -A 10 "external:\s*\[" apps/desktop/electron.vite.config.ts

echo
echo "=== Search for handling of `@ast-grep/napi` and mastracode ==="
rg -rn "@ast-grep/napi|mastracode" --type ts --type json --type yml --type yaml apps/

echo
echo "=== Check electron-builder config if exists ==="
fd "electron-builder" apps/desktop/ -t f -x cat {}

echo
echo "=== Look for package.json scripts and build config ==="
cat apps/desktop/package.json | head -50

Repository: superset-sh/superset

Length of output: 3226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Inspect electron-builder.ts configuration ==="
head -200 apps/desktop/electron-builder.ts

echo
echo "=== Check for extraResources, asarUnpack, or files configuration ==="
rg -n "extraResources|asarUnpack|files|nativeModules" apps/desktop/electron-builder.ts

echo
echo "=== Look for mastracode and `@ast-grep/napi` in package.json ==="
rg -A2 -B2 "mastracode|@ast-grep/napi" apps/desktop/package.json

echo
echo "=== Check if `@ast-grep/napi` or mastracode are symlinks in node_modules ==="
ls -la apps/desktop/node_modules/@ast-grep 2>/dev/null | head -20 || echo "Directory check skipped"
ls -la apps/desktop/node_modules/mastracode 2>/dev/null | head -20 || echo "Directory check skipped"

echo
echo "=== Verify copy-native-modules.ts is run as part of build ==="
rg -n "copy-native-modules" apps/desktop/package.json

Repository: superset-sh/superset

Length of output: 6055


Add mastracode and @ast-grep/napi to electron-builder packaging rules.

These modules are externalized in rollupOptions.external but are not included in the files configuration in electron-builder.ts. They will not be copied to the packaged application, causing runtime failures when the app attempts to load them.

Add both modules to the files configuration in electron-builder.ts (alongside better-sqlite3, node-pty, and friendly-words):

Example addition to electron-builder.ts files config
{
	from: "node_modules/mastracode",
	to: "node_modules/mastracode",
	filter: ["**/*"],
},
{
	from: "node_modules/@ast-grep/napi",
	to: "node_modules/@ast-grep/napi",
	filter: ["**/*"],
},

If either module contains native binaries, also add them to the asarUnpack configuration so electron-builder unpacks them outside the asar archive.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/electron.vite.config.ts` around lines 105 - 112, The build
packaging is missing mastracode and `@ast-grep/napi` so they are externalized by
rollup (see rollupOptions.external) but not copied into the packaged app; update
the electron-builder files configuration in electron-builder.ts to add entries
for "node_modules/mastracode" and "node_modules/@ast-grep/napi" (same pattern
used for better-sqlite3, node-pty, friendly-words) so those folders are included
in the distributable, and if either contains native binaries add their paths to
the asarUnpack config so native modules are unpacked outside the ASAR.

plugins: [sentryPlugin].filter(Boolean),
},
},
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
},
"dependencies": {
"@ai-sdk/react": "^3.0.0",
"@ast-grep/napi": "^0.41.0",
"@better-auth/stripe": "1.4.18",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
Expand Down Expand Up @@ -140,6 +141,7 @@
"lowdb": "^7.0.1",
"lowlight": "^3.3.0",
"lucide-react": "^0.563.0",
"mastracode": "^0.3.0",
"monaco-editor": "^0.55.1",
"nanoid": "^5.1.6",
"node-addon-api": "^7.1.0",
Expand Down
23 changes: 23 additions & 0 deletions apps/desktop/src/lib/trpc/routers/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
DEFAULT_SHOW_PRESETS_BAR,
DEFAULT_SHOW_RESOURCE_MONITOR,
DEFAULT_TERMINAL_LINK_BEHAVIOR,
DEFAULT_USE_COMPACT_TERMINAL_ADD_BUTTON,
} from "shared/constants";
import {
CUSTOM_RINGTONE_ID,
Expand Down Expand Up @@ -412,6 +413,28 @@ export const createSettingsRouter = () => {
return { success: true };
}),

getUseCompactTerminalAddButton: publicProcedure.query(() => {
const row = getSettings();
const useBigAddButton =
row.useBigTerminalAddButton ?? !DEFAULT_USE_COMPACT_TERMINAL_ADD_BUTTON;
return !useBigAddButton;
}),

setUseCompactTerminalAddButton: publicProcedure
.input(z.object({ enabled: z.boolean() }))
.mutation(({ input }) => {
localDb
.insert(settings)
.values({ id: 1, useBigTerminalAddButton: !input.enabled })
.onConflictDoUpdate({
target: settings.id,
set: { useBigTerminalAddButton: !input.enabled },
})
.run();

return { success: true };
}),

getTerminalLinkBehavior: publicProcedure.query(() => {
const row = getSettings();
return row.terminalLinkBehavior ?? DEFAULT_TERMINAL_LINK_BEHAVIOR;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import type { TerminalPreset } from "@superset/local-db";
import { FEATURE_FLAGS } from "@superset/shared/constants";
import { Button } from "@superset/ui/button";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@superset/ui/dropdown-menu";
import { useLiveQuery } from "@tanstack/react-db";
import { useParams } from "@tanstack/react-router";
import { useNavigate, useParams } from "@tanstack/react-router";
import { useFeatureFlagEnabled } from "posthog-js/react";
import {
useCallback,
Expand All @@ -19,21 +11,22 @@ import {
useRef,
useState,
} from "react";
import { BsTerminalPlus } from "react-icons/bs";
import { LuPlus } from "react-icons/lu";
import { TbMessageCirclePlus, TbWorld } from "react-icons/tb";
import { HotkeyMenuShortcut } from "renderer/components/HotkeyMenuShortcut";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { usePresets } from "renderer/react-query/presets";
import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider";
import { useTabsStore } from "renderer/stores/tabs/store";
import { useTabsWithPresets } from "renderer/stores/tabs/useTabsWithPresets";
import {
isLastPaneInTab,
resolveActiveTabIdForWorkspace,
} from "renderer/stores/tabs/utils";
import {
DEFAULT_SHOW_PRESETS_BAR,
DEFAULT_USE_COMPACT_TERMINAL_ADD_BUTTON,
} from "shared/constants";
import { type ActivePaneStatus, pickHigherStatus } from "shared/tabs-types";
import { AddTabButton } from "./components/AddTabButton";
import { GroupItem } from "./GroupItem";
import { NewTabDropZone } from "./NewTabDropZone";

export function GroupStrip() {
const { workspaceId: activeWorkspaceId } = useParams({ strict: false });
Expand All @@ -42,7 +35,7 @@ export function GroupStrip() {
const panes = useTabsStore((s) => s.panes);
const activeTabIds = useTabsStore((s) => s.activeTabIds);
const tabHistoryStacks = useTabsStore((s) => s.tabHistoryStacks);
const { addTab } = useTabsWithPresets();
const { addTab, openPreset } = useTabsWithPresets();
const addChatMastraTab = useTabsStore((s) => s.addChatMastraTab);
const addBrowserTab = useTabsStore((s) => s.addBrowserTab);
const renameTab = useTabsStore((s) => s.renameTab);
Expand All @@ -53,6 +46,8 @@ export function GroupStrip() {
const reorderTabs = useTabsStore((s) => s.reorderTabs);

const setTabAutoTitle = useTabsStore((s) => s.setTabAutoTitle);
const { presets } = usePresets();
const navigate = useNavigate();

const hasAiChat = useFeatureFlagEnabled(FEATURE_FLAGS.AI_CHAT);
const scrollContainerRef = useRef<HTMLDivElement>(null);
Expand All @@ -61,6 +56,8 @@ export function GroupStrip() {
const utils = electronTrpc.useUtils();
const { data: showPresetsBar } =
electronTrpc.settings.getShowPresetsBar.useQuery();
const { data: useCompactTerminalAddButton } =
electronTrpc.settings.getUseCompactTerminalAddButton.useQuery();
const setShowPresetsBar = electronTrpc.settings.setShowPresetsBar.useMutation(
{
onMutate: async ({ enabled }) => {
Expand All @@ -79,6 +76,30 @@ export function GroupStrip() {
},
},
);
const setUseCompactTerminalAddButton =
electronTrpc.settings.setUseCompactTerminalAddButton.useMutation({
onMutate: async ({ enabled }) => {
await utils.settings.getUseCompactTerminalAddButton.cancel();
const previous =
utils.settings.getUseCompactTerminalAddButton.getData();
utils.settings.getUseCompactTerminalAddButton.setData(
undefined,
enabled,
);
return { previous };
},
onError: (_err, _vars, context) => {
if (context?.previous !== undefined) {
utils.settings.getUseCompactTerminalAddButton.setData(
undefined,
context.previous,
);
}
},
onSettled: () => {
utils.settings.getUseCompactTerminalAddButton.invalidate();
},
});

const tabs = useMemo(
() =>
Expand Down Expand Up @@ -160,6 +181,18 @@ export function GroupStrip() {
addBrowserTab(activeWorkspaceId);
};

const handleOpenPreset = useCallback(
(preset: TerminalPreset) => {
if (!activeWorkspaceId) return;
openPreset(activeWorkspaceId, preset, { target: "active-tab" });
},
[activeWorkspaceId, openPreset],
);
Comment on lines +184 to +190
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preset selection currently targets the active tab instead of creating a new tab.

At Line 180, openPreset(..., { target: "active-tab" }) makes preset selection behave differently from the other add actions in this menu (Terminal/Chat/Browser), which are tab-creation oriented.

Proposed fix
-			openPreset(activeWorkspaceId, preset, { target: "active-tab" });
+			openPreset(activeWorkspaceId, preset, { target: "new-tab" });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleOpenPreset = useCallback(
(preset: TerminalPreset) => {
if (!activeWorkspaceId) return;
openPreset(activeWorkspaceId, preset, { target: "active-tab" });
},
[activeWorkspaceId, openPreset],
);
const handleOpenPreset = useCallback(
(preset: TerminalPreset) => {
if (!activeWorkspaceId) return;
openPreset(activeWorkspaceId, preset, { target: "new-tab" });
},
[activeWorkspaceId, openPreset],
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/GroupStrip/GroupStrip.tsx`
around lines 177 - 183, handleOpenPreset currently opens a preset into the
active tab by calling openPreset(activeWorkspaceId, preset, { target:
"active-tab" }); change this to create a new tab to match other "add" actions —
call openPreset with the appropriate new-tab target (e.g. { target: "new-tab" }
or the project’s equivalent) using the same activeWorkspaceId and preset; update
the call inside handleOpenPreset (function name: handleOpenPreset, called
symbol: openPreset, param type: TerminalPreset, guard: activeWorkspaceId) and
keep the existing useCallback dependency array unchanged.


const handleOpenPresetsSettings = useCallback(() => {
navigate({ to: "/settings/presets" });
}, [navigate]);

const handleSelectGroup = (tabId: string) => {
if (activeWorkspaceId) {
setActiveTab(activeWorkspaceId, tabId);
Expand Down Expand Up @@ -219,52 +252,29 @@ export function GroupStrip() {
requestAnimationFrame(updateOverflow);
}, [updateOverflow]);

const useCompactAddButton =
useCompactTerminalAddButton ?? DEFAULT_USE_COMPACT_TERMINAL_ADD_BUTTON;

const plusControl = (
<NewTabDropZone
onDrop={(paneId) => movePaneToNewTab(paneId)}
<AddTabButton
hasAiChat={hasAiChat === true}
useCompactAddButton={useCompactAddButton}
showPresetsBar={showPresetsBar ?? DEFAULT_SHOW_PRESETS_BAR}
presets={presets}
onDropToNewTab={movePaneToNewTab}
isLastPaneInTab={checkIsLastPaneInTab}
>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-7 px-1 shrink-0 rounded-md border border-border/60 bg-muted/30 text-muted-foreground hover:bg-accent/60 hover:text-foreground"
>
<LuPlus className="size-3.5" strokeWidth={1.8} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuItem onClick={handleAddGroup} className="gap-2">
<BsTerminalPlus className="size-4" />
<span>Terminal</span>
<HotkeyMenuShortcut hotkeyId="NEW_GROUP" />
</DropdownMenuItem>
{hasAiChat && (
<DropdownMenuItem onClick={handleAddChat} className="gap-2">
<TbMessageCirclePlus className="size-4" />
<span>Chat</span>
<HotkeyMenuShortcut hotkeyId="NEW_CHAT" />
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={handleAddBrowser} className="gap-2">
<TbWorld className="size-4" />
<span>Browser</span>
<HotkeyMenuShortcut hotkeyId="NEW_BROWSER" />
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem
checked={showPresetsBar ?? false}
onCheckedChange={(checked) =>
setShowPresetsBar.mutate({ enabled: checked })
}
onSelect={(e) => e.preventDefault()}
>
Show Preset Bar
</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>
</NewTabDropZone>
onAddTerminal={handleAddGroup}
onAddChat={handleAddChat}
onAddBrowser={handleAddBrowser}
onOpenPreset={handleOpenPreset}
onConfigurePresets={handleOpenPresetsSettings}
onToggleShowPresetsBar={(enabled) =>
setShowPresetsBar.mutate({ enabled })
}
onToggleCompactAddButton={(enabled) =>
setUseCompactTerminalAddButton.mutate({ enabled })
}
/>
);

return (
Expand Down Expand Up @@ -301,7 +311,15 @@ export function GroupStrip() {
</div>
)}
{hasHorizontalOverflow ? (
<div className="h-full w-10 shrink-0" />
<div
className={`h-full shrink-0 ${
!useCompactAddButton
? hasAiChat
? "w-[220px]"
: "w-[170px]"
: "w-10"
}`}
/>
) : (
<div className="shrink-0">{plusControl}</div>
)}
Expand Down
Loading
Loading