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
22 changes: 22 additions & 0 deletions apps/desktop/src/lib/trpc/routers/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
BRANCH_SORT_ORDERS,
EXECUTION_MODES,
EXTERNAL_APPS,
FILE_DRAG_BEHAVIORS,
FILE_OPEN_MODES,
NON_EDITOR_APPS,
POST_COMMIT_COMMANDS,
Expand All @@ -29,6 +30,7 @@ import {
DEFAULT_AUTO_APPLY_DEFAULT_PRESET,
DEFAULT_CONFIRM_ON_QUIT,
DEFAULT_EXPOSE_HOST_SERVICE_VIA_RELAY,
DEFAULT_FILE_DRAG_BEHAVIOR,
DEFAULT_FILE_OPEN_MODE,
DEFAULT_OPEN_LINKS_IN_APP,
DEFAULT_PREVENT_AGENT_SLEEP,
Expand Down Expand Up @@ -731,6 +733,11 @@ export const createSettingsRouter = () => {
return row.fileOpenMode ?? DEFAULT_FILE_OPEN_MODE;
}),

getFileDragBehavior: publicProcedure.query(() => {
const row = getSettings();
return row.fileDragBehavior ?? DEFAULT_FILE_DRAG_BEHAVIOR;
}),

setFileOpenMode: publicProcedure
.input(z.object({ mode: z.enum(FILE_OPEN_MODES) }))
.mutation(({ input }) => {
Expand All @@ -746,6 +753,21 @@ export const createSettingsRouter = () => {
return { success: true };
}),

setFileDragBehavior: publicProcedure
.input(z.object({ behavior: z.enum(FILE_DRAG_BEHAVIORS) }))
.mutation(({ input }) => {
localDb
.insert(settings)
.values({ id: 1, fileDragBehavior: input.behavior })
.onConflictDoUpdate({
target: settings.id,
set: { fileDragBehavior: input.behavior },
})
.run();

return { success: true };
}),

getRightSidebarOpenViewWidth: publicProcedure.query(() => {
const row = getSettings();
return (
Expand Down
5 changes: 3 additions & 2 deletions apps/desktop/src/main/todo-agent/schedule-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ async function resolveDefaultBranch(cwd: string): Promise<string> {
10_000,
);
const ref = stdout.trim();
if (ref.startsWith("origin/")) {
return ref.slice("origin/".length);
const defaultBranch = ref.replace(/^origin\//, "");
if (defaultBranch) {
return defaultBranch;
}
} catch {}
// Fallbacks — conservative default.
Expand Down
11 changes: 11 additions & 0 deletions apps/desktop/src/renderer/lib/file-drag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const INTERNAL_FILE_PATH_MIME = "application/x-superset-file-path";

export function getInternalDraggedFilePath(
dataTransfer: DataTransfer,
): string | null {
if (!Array.from(dataTransfer.types).includes(INTERNAL_FILE_PATH_MIME)) {
return null;
}

return dataTransfer.getData(INTERNAL_FILE_PATH_MIME) || null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
useSyncExternalStore,
} from "react";
import { useHotkey } from "renderer/hotkeys";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { getInternalDraggedFilePath } from "renderer/lib/file-drag";
import {
type ConnectionState,
terminalRuntimeRegistry,
Expand All @@ -25,6 +27,10 @@ import { ScrollToBottomButton } from "renderer/screens/main/components/Workspace
import { TerminalSearch } from "renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/TerminalSearch";
import { useTheme } from "renderer/stores/theme";
import { resolveTerminalThemeType } from "renderer/stores/theme/utils";
import {
DEFAULT_FILE_DRAG_BEHAVIOR,
DEFAULT_FILE_OPEN_MODE,
} from "shared/constants";
import { LinkHoverTooltip } from "./components/LinkHoverTooltip";
import { useLinkHoverState } from "./hooks/useLinkHoverState";
import { useTerminalAppearance } from "./hooks/useTerminalAppearance";
Expand Down Expand Up @@ -53,6 +59,10 @@ export function TerminalPane({
onRevealPath,
}: TerminalPaneProps) {
const openInExternalEditor = useOpenInExternalEditor(workspaceId);
const { data: fileDragBehavior } =
electronTrpc.settings.getFileDragBehavior.useQuery();
const { data: fileOpenMode } =
electronTrpc.settings.getFileOpenMode.useQuery();
const {
hoveredLink,
onHover: onLinkHover,
Expand Down Expand Up @@ -284,6 +294,19 @@ export function TerminalPane({
dragCounterRef.current = 0;
setIsDropActive(false);
if (connectionState === "closed") return;
const hasNativeFiles = event.dataTransfer.files.length > 0;
const internalFilePath = getInternalDraggedFilePath(event.dataTransfer);
if (
!hasNativeFiles &&
internalFilePath &&
(fileDragBehavior ?? DEFAULT_FILE_DRAG_BEHAVIOR) === "open-file-viewer"
) {
Comment on lines +300 to +303
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip file-viewer open for directory drags

This condition treats any internal sidebar drag as a file and immediately routes it to onOpenFile, but folder rows are also draggable in the Files sidebar (FileTreeItem wires useFileDrag for every entry.path). Dragging a directory into a terminal now opens a file pane that resolves to an "is directory" error and never pastes the path, which regresses the prior drag-to-terminal workflow for directories; this branch should verify the dropped path is a file (or fall back to paste behavior for directories).

Useful? React with 👍 / 👎.

onOpenFile(
internalFilePath,
(fileOpenMode ?? DEFAULT_FILE_OPEN_MODE) === "new-tab",
);
return;
}
const text = resolveDroppedText(event.dataTransfer);
if (!text) return;
terminalRuntimeRegistry.getTerminal(terminalId)?.focus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,20 @@ function WorkspaceContent({
);

// FORK NOTE: fork's openFilePane takes (filePath, displayName?) for the
// memo-title path; usePaneRegistry's onOpenFile contract is
// (path, openInNewTab?). Bind without forwarding the 2nd arg so the types
// line up — terminal Cmd+click just opens in the active tab — and wrap
// with useCallback so paneRegistry's memo stays stable.
// memo-title path. Pane registry callers that pass an explicit
// `openInNewTab` use the sidebar-opening path so they can honor the
// file-open preference and right-sidebar split width, while callers that
// omit the 2nd arg keep the legacy "open in the active pane" behavior.
const handleTerminalOpenFile = useCallback(
(filePath: string) => openFilePane(filePath),
[openFilePane],
(filePath: string, openInNewTab?: boolean) => {
if (openInNewTab !== undefined) {
openSidebarFilePane(filePath, openInNewTab);
return;
}

openFilePane(filePath);
},
[openFilePane, openSidebarFilePane],
);
Comment thread
MocA-Love marked this conversation as resolved.

const paneRegistry = usePaneRegistry(workspaceId, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FileOpenMode } from "@superset/local-db";
import type { FileDragBehavior, FileOpenMode } from "@superset/local-db";
import { Input } from "@superset/ui/input";
import { Label } from "@superset/ui/label";
import {
Expand Down Expand Up @@ -44,6 +44,10 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) {
SETTING_ITEM_ID.BEHAVIOR_FILE_OPEN_MODE,
visibleItems,
);
const showFileDragBehavior = isItemVisible(
SETTING_ITEM_ID.BEHAVIOR_FILE_DRAG_BEHAVIOR,
visibleItems,
);
const showRightSidebarOpenViewWidth = isItemVisible(
SETTING_ITEM_ID.BEHAVIOR_RIGHT_SIDEBAR_OPEN_VIEW_WIDTH,
visibleItems,
Expand Down Expand Up @@ -152,6 +156,28 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) {
utils.settings.getFileOpenMode.invalidate();
},
});
const { data: fileDragBehavior, isLoading: isFileDragBehaviorLoading } =
electronTrpc.settings.getFileDragBehavior.useQuery();
const setFileDragBehavior =
electronTrpc.settings.setFileDragBehavior.useMutation({
onMutate: async ({ behavior }) => {
await utils.settings.getFileDragBehavior.cancel();
const previous = utils.settings.getFileDragBehavior.getData();
utils.settings.getFileDragBehavior.setData(undefined, behavior);
return { previous };
},
onError: (_err, _vars, context) => {
if (context?.previous !== undefined) {
utils.settings.getFileDragBehavior.setData(
undefined,
context.previous,
);
}
},
onSettled: () => {
utils.settings.getFileDragBehavior.invalidate();
},
});

const {
data: rightSidebarOpenViewWidth,
Expand Down Expand Up @@ -329,6 +355,42 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) {
</div>
)}

{showFileDragBehavior && (
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-sm font-medium">
Sidebar file drag behavior
</Label>
<p className="text-xs text-muted-foreground">
Choose whether dragging files from the Files or Changes sidebar
into the main area opens them in the file viewer or pastes their
path into terminals
</p>
</div>
<Select
value={fileDragBehavior ?? "open-file-viewer"}
onValueChange={(value) =>
setFileDragBehavior.mutate({
behavior: value as FileDragBehavior,
})
}
disabled={
isFileDragBehaviorLoading || setFileDragBehavior.isPending
}
>
<SelectTrigger className="w-[220px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="open-file-viewer">
Open file viewer
</SelectItem>
<SelectItem value="paste-path">Paste file path</SelectItem>
</SelectContent>
</Select>
</div>
)}

{showRightSidebarOpenViewWidth && (
<div className="flex items-center justify-between gap-6">
<div className="space-y-0.5">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,6 @@ export function AudioEditor({
.then((data) => {
if (!cancelled) {
setWaveform(data);
if (!hasInitialRange) {
setStartSeconds(0);
setEndSeconds(Math.min(10, data.duration));
}
}
})
.catch((err) => {
Expand All @@ -236,6 +232,12 @@ export function AudioEditor({
};
}, [audioUrl]);

useEffect(() => {
if (!waveform || hasInitialRange) return;
setStartSeconds(0);
setEndSeconds(Math.min(10, waveform.duration));
}, [waveform, hasInitialRange]);

// Draw waveform whenever relevant state changes
const redrawWaveform = useCallback(() => {
if (!canvasRef.current || !waveform) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ export function EditCustomRingtoneDialog({
undefined,
{ enabled: open, staleTime: 0 },
);
const openSource = electronTrpc.ringtone.openCustomSource.useMutation();
const closeSource = electronTrpc.ringtone.closeCustomSource.useMutation();
const { mutateAsync: openCustomSource } =
electronTrpc.ringtone.openCustomSource.useMutation();
const { mutate: closeCustomSource } =
electronTrpc.ringtone.closeCustomSource.useMutation();
const reEdit = electronTrpc.ringtone.reEditCustom.useMutation();

const [tempId, setTempId] = useState<string | null>(null);
Expand All @@ -56,8 +58,7 @@ export function EditCustomRingtoneDialog({
useEffect(() => {
if (!open) return;
let cancelled = false;
openSource
.mutateAsync()
openCustomSource()
.then((result) => {
if (cancelled) return;
if (result.tempId) {
Expand All @@ -75,22 +76,20 @@ export function EditCustomRingtoneDialog({
return () => {
cancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
}, [open, openCustomSource]);

// Release the tempId when the dialog closes.
useEffect(() => {
if (open) return;
const id = openedTempIdRef.current;
if (id) {
closeSource.mutate({ tempId: id });
closeCustomSource({ tempId: id });
openedTempIdRef.current = null;
}
setTempId(null);
setErrorMessage(null);
setDisplayName(currentDisplayName);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
}, [open, closeCustomSource, currentDisplayName]);

useEffect(() => {
if (open) setDisplayName(currentDisplayName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const SETTING_ITEM_ID = {
BEHAVIOR_TELEMETRY: "behavior-telemetry",
BEHAVIOR_PREVENT_AGENT_SLEEP: "behavior-prevent-agent-sleep",
BEHAVIOR_FILE_OPEN_MODE: "behavior-file-open-mode",
BEHAVIOR_FILE_DRAG_BEHAVIOR: "behavior-file-drag-behavior",
BEHAVIOR_RIGHT_SIDEBAR_OPEN_VIEW_WIDTH:
"behavior-right-sidebar-open-view-width",
BEHAVIOR_RESOURCE_MONITOR: "behavior-resource-monitor",
Expand Down Expand Up @@ -612,6 +613,26 @@ export const SETTINGS_ITEMS: SettingsItem[] = [
"behavior",
],
},
{
id: SETTING_ITEM_ID.BEHAVIOR_FILE_DRAG_BEHAVIOR,
section: "behavior",
title: "Sidebar file drag behavior",
description:
"Choose whether dragging files from the Files or Changes sidebar opens the file viewer or pastes file paths into terminals",
keywords: [
"file",
"drag",
"drop",
"drag and drop",
"sidebar",
"files",
"changes",
"terminal",
"path",
"viewer",
"behavior",
],
},
{
id: SETTING_ITEM_ID.BEHAVIOR_RIGHT_SIDEBAR_OPEN_VIEW_WIDTH,
section: "behavior",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import type { Terminal as XTerm } from "@xterm/xterm";
import "@xterm/xterm/css/xterm.css";
import { memo, useCallback, useEffect, useRef, useState } from "react";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { getInternalDraggedFilePath } from "renderer/lib/file-drag";
import { buildTerminalCommand } from "renderer/lib/terminal/launch-command";
import { useTabsStore } from "renderer/stores/tabs/store";
import { useTerminalSuggestionsStore } from "renderer/stores/terminal-suggestions";
import { useEffectiveTerminalTheme } from "renderer/stores/vibrancy";
import { DEFAULT_FILE_DRAG_BEHAVIOR } from "shared/constants";
import { sanitizeForTitle } from "./commandBuffer";
import { SessionKilledOverlay } from "./components";
import {
Expand Down Expand Up @@ -54,6 +56,9 @@ export const Terminal = memo(function Terminal({
const isWorkspaceRunPane = Boolean(pane?.workspaceRun?.workspaceId);
const paneInitialCwd = pane?.initialCwd;
const clearPaneInitialData = useTabsStore((s) => s.clearPaneInitialData);
const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane);
const { data: fileDragBehavior } =
electronTrpc.settings.getFileDragBehavior.useQuery();

const { data: workspaceData } = electronTrpc.workspaces.get.useQuery(
{ id: workspaceId },
Expand Down Expand Up @@ -562,6 +567,19 @@ export const Terminal = memo(function Terminal({
const handleDrop = (event: React.DragEvent) => {
event.preventDefault();
const files = Array.from(event.dataTransfer.files);
const internalFilePath = getInternalDraggedFilePath(event.dataTransfer);
if (
files.length === 0 &&
internalFilePath &&
(fileDragBehavior ?? DEFAULT_FILE_DRAG_BEHAVIOR) === "open-file-viewer"
) {
addFileViewerPane(workspaceId, {
filePath: internalFilePath,
useRightSidebarOpenViewWidth: true,
});
return;
}

let text: string;
if (files.length > 0) {
// Native file drop (from Finder, etc.)
Expand Down
Loading
Loading