Skip to content

Commit 0574a6a

Browse files
committed
feat(platform): 优化Windows平台路径处理和SSH配置文件支持
- 在SSH配置解析中添加Windows系统配置文件支持 - 实现Windows驱动器根目录和UNC路径的正确处理 - 修复历史导航中的路径分隔符问题 - 添加Windows虚拟根目录的支持 - 增强文件系统操作的跨平台兼容性 - 为终端滚动添加滚轮事件处理功能 - 实现对创建条目的权限控制检查
1 parent 263eda4 commit 0574a6a

19 files changed

Lines changed: 609 additions & 72 deletions

.idea/.gitignore

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/prettier.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/waveterm.iml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

electron-builder.config.cjs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ const fs = require("fs");
44
const path = require("path");
55

66
const windowsShouldSign = !!process.env.SM_CODE_SIGNING_CERT_SHA1_HASH;
7+
const windowsShouldEditExecutable = windowsShouldSign || process.env.WAVETERM_WINDOWS_EDIT_EXECUTABLE === "1";
8+
const windowsShouldBuildInstallers = windowsShouldSign || process.env.WAVETERM_WINDOWS_INSTALLERS === "1";
9+
const windowsTargets = windowsShouldBuildInstallers ? ["nsis", "msi", "zip"] : ["zip"];
10+
const localWindowsElectronDist = path.resolve(__dirname, "node_modules", "electron", "dist");
11+
const useLocalWindowsElectronDist =
12+
process.platform === "win32" && fs.existsSync(path.join(localWindowsElectronDist, "electron.exe"));
713

814
/**
915
* @type {import('electron-builder').Configuration}
@@ -18,6 +24,7 @@ const config = {
1824
npmRebuild: false,
1925
nodeGypRebuild: false,
2026
electronCompile: false,
27+
electronDist: useLocalWindowsElectronDist ? localWindowsElectronDist : null,
2128
files: [
2229
{
2330
from: "./dist",
@@ -96,7 +103,8 @@ const config = {
96103
afterInstall: "build/deb-postinstall.tpl",
97104
},
98105
win: {
99-
target: ["nsis", "msi", "zip"],
106+
target: windowsTargets,
107+
signAndEditExecutable: windowsShouldEditExecutable,
100108
signtoolOptions: windowsShouldSign && {
101109
signingHashAlgorithms: ["sha256"],
102110
publisherName: "Command Line Inc",

emain/emain-platform.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ function getWaveConfigDir(): string {
117117
retVal = override;
118118
} else if (xdgConfigHome) {
119119
retVal = path.join(xdgConfigHome, waveDirName);
120+
} else if (unamePlatform === "win32") {
121+
const legacyConfigDir = path.join(app.getPath("home"), ".config", waveDirName);
122+
retVal = existsSync(legacyConfigDir) ? legacyConfigDir : paths.config;
120123
} else {
121124
retVal = path.join(app.getPath("home"), ".config", waveDirName);
122125
}

frontend/app/view/preview/preview-directory.tsx

Lines changed: 76 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ function DirectoryTable({
187187
);
188188

189189
const setEntryManagerProps = useSetAtom(entryManagerOverlayPropsAtom);
190+
const canCreateEntries = useAtomValue(model.statFile).supportsmkdir ?? true;
190191

191192
const updateName = useCallback(
192193
(path: string, isDir: boolean) => {
@@ -228,8 +229,8 @@ function DirectoryTable({
228229
enableSortingRemoval: false,
229230
meta: {
230231
updateName,
231-
newFile,
232-
newDirectory,
232+
newFile: canCreateEntries ? newFile : () => {},
233+
newDirectory: canCreateEntries ? newDirectory : () => {},
233234
},
234235
});
235236
const sortingState = table.getState().sorting;
@@ -326,6 +327,7 @@ function TableBody({
326327
const dummyLineRef = useRef<HTMLDivElement>(null);
327328
const warningBoxRef = useRef<HTMLDivElement>(null);
328329
const conn = useAtomValue(model.connection);
330+
const canCreateEntries = useAtomValue(model.statFile).supportsmkdir ?? true;
329331
const setErrorMsg = useSetAtom(model.errorMsgAtom);
330332

331333
useEffect(() => {
@@ -368,28 +370,33 @@ function TableBody({
368370
return;
369371
}
370372
const fileName = finfo.path.split("/").pop();
371-
const menu: ContextMenuItem[] = [
372-
{
373-
label: "New File",
374-
click: () => {
375-
table.options.meta.newFile();
373+
const menu: ContextMenuItem[] = [];
374+
if (canCreateEntries) {
375+
menu.push(
376+
{
377+
label: "New File",
378+
click: () => {
379+
table.options.meta.newFile();
380+
},
376381
},
377-
},
378-
{
379-
label: "New Folder",
380-
click: () => {
381-
table.options.meta.newDirectory();
382+
{
383+
label: "New Folder",
384+
click: () => {
385+
table.options.meta.newDirectory();
386+
},
382387
},
383-
},
384-
{
385-
label: "Rename",
386-
click: () => {
387-
table.options.meta.updateName(finfo.path, finfo.isdir);
388+
{
389+
label: "Rename",
390+
click: () => {
391+
table.options.meta.updateName(finfo.path, finfo.isdir);
392+
},
388393
},
389-
},
390-
{
391-
type: "separator",
392-
},
394+
{
395+
type: "separator",
396+
}
397+
);
398+
}
399+
menu.push(
393400
{
394401
label: "Copy File Name",
395402
click: () => fireAndForget(() => navigator.clipboard.writeText(fileName)),
@@ -405,28 +412,30 @@ function TableBody({
405412
{
406413
label: "Copy Full File Name (Shell Quoted)",
407414
click: () => fireAndForget(() => navigator.clipboard.writeText(shellQuote([finfo.path]))),
408-
},
409-
];
410-
addOpenMenuItems(menu, conn, finfo);
411-
menu.push(
412-
{
413-
type: "separator",
414-
},
415-
{
416-
label: "Default Settings",
417-
submenu: makeDirectoryDefaultMenuItems(model),
418-
},
419-
{
420-
type: "separator",
421-
},
422-
{
423-
label: "Delete",
424-
click: () => handleFileDelete(model, finfo.path, false, setErrorMsg),
425415
}
426416
);
417+
addOpenMenuItems(menu, conn, finfo);
418+
menu.push({
419+
type: "separator",
420+
});
421+
menu.push({
422+
label: "Default Settings",
423+
submenu: makeDirectoryDefaultMenuItems(model),
424+
});
425+
if (canCreateEntries) {
426+
menu.push(
427+
{
428+
type: "separator",
429+
},
430+
{
431+
label: "Delete",
432+
click: () => handleFileDelete(model, finfo.path, false, setErrorMsg),
433+
}
434+
);
435+
}
427436
ContextMenuModel.getInstance().showContextMenu(menu, e);
428437
},
429-
[setRefreshVersion, conn]
438+
[canCreateEntries, setRefreshVersion, conn]
430439
);
431440

432441
const allRows = table.getRowModel().flatRows;
@@ -571,6 +580,7 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
571580
const conn = useAtomValue(model.connection);
572581
const blockData = useAtomValue(model.blockAtom);
573582
const finfo = useAtomValue(model.statFile);
583+
const canCreateEntries = finfo?.supportsmkdir ?? true;
574584
const dirPath = finfo?.path;
575585
const setErrorMsg = useSetAtom(model.errorMsgAtom);
576586

@@ -796,6 +806,9 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
796806
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
797807

798808
const newFile = useCallback(() => {
809+
if (!canCreateEntries) {
810+
return;
811+
}
799812
setEntryManagerProps({
800813
entryManagerType: EntryManagerType.NewFile,
801814
onSave: (newName: string) => {
@@ -815,8 +828,11 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
815828
setEntryManagerProps(undefined);
816829
},
817830
});
818-
}, [dirPath]);
831+
}, [canCreateEntries, dirPath]);
819832
const newDirectory = useCallback(() => {
833+
if (!canCreateEntries) {
834+
return;
835+
}
820836
setEntryManagerProps({
821837
entryManagerType: EntryManagerType.NewDirectory,
822838
onSave: (newName: string) => {
@@ -832,34 +848,37 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
832848
setEntryManagerProps(undefined);
833849
},
834850
});
835-
}, [dirPath]);
851+
}, [canCreateEntries, dirPath]);
836852

837853
const handleFileContextMenu = useCallback(
838854
(e: any) => {
839855
e.preventDefault();
840856
e.stopPropagation();
841-
const menu: ContextMenuItem[] = [
842-
{
843-
label: "New File",
844-
click: () => {
845-
newFile();
857+
const menu: ContextMenuItem[] = [];
858+
if (canCreateEntries) {
859+
menu.push(
860+
{
861+
label: "New File",
862+
click: () => {
863+
newFile();
864+
},
846865
},
847-
},
848-
{
849-
label: "New Folder",
850-
click: () => {
851-
newDirectory();
866+
{
867+
label: "New Folder",
868+
click: () => {
869+
newDirectory();
870+
},
852871
},
853-
},
854-
{
855-
type: "separator",
856-
},
857-
];
872+
{
873+
type: "separator",
874+
}
875+
);
876+
}
858877
addOpenMenuItems(menu, conn, finfo);
859878

860879
ContextMenuModel.getInstance().showContextMenu(menu, e);
861880
},
862-
[setRefreshVersion, conn, newFile, newDirectory, dirPath]
881+
[canCreateEntries, setRefreshVersion, conn, newFile, newDirectory, dirPath]
863882
);
864883

865884
return (

0 commit comments

Comments
 (0)