mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8423334cde | |||
| bc057876a1 | |||
| d344fd9b6d | |||
| 31414a7536 | |||
| e281cebb56 | |||
| 65c446e4a1 | |||
| 047513bf3a | |||
| 78f838439a | |||
| 470dfb19b7 | |||
| ade5bc01b3 | |||
| c768616a24 | |||
| bb7ea131b4 | |||
| 0b2fdf0ca0 | |||
| eced7120c7 | |||
| 4395c7410d | |||
| b0c5443916 | |||
| 85ec6d45fe | |||
| fd97f895d0 | |||
| 071a051382 |
@@ -58,6 +58,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: "Push version bump: ${{ steps.bump-version.outputs.WAVETERM_VERSION }}"
|
||||
if: github.ref_protected
|
||||
run: |
|
||||
# Create a new commit for the package version bump in package.json
|
||||
export VERSION=${{ steps.bump-version.outputs.WAVETERM_VERSION }}
|
||||
|
||||
@@ -10,7 +10,7 @@ const path = require("path");
|
||||
const config = {
|
||||
appId: pkg.build.appId,
|
||||
productName: pkg.productName,
|
||||
executableName: pkg.name,
|
||||
executableName: pkg.productName,
|
||||
artifactName: "${productName}-${platform}-${arch}-${version}.${ext}",
|
||||
generateUpdatesFilesForAllChannels: true,
|
||||
npmRebuild: false,
|
||||
@@ -55,6 +55,7 @@ const config = {
|
||||
linux: {
|
||||
artifactName: "${name}-${platform}-${arch}-${version}.${ext}",
|
||||
category: "TerminalEmulator",
|
||||
executableName: pkg.name,
|
||||
icon: "build/icons.icns",
|
||||
target: ["zip", "deb", "rpm", "AppImage", "pacman"],
|
||||
synopsis: pkg.description,
|
||||
@@ -86,7 +87,7 @@ const config = {
|
||||
if (context.electronPlatformName === "darwin" && context.arch === Arch.universal) {
|
||||
const packageBinDir = path.resolve(
|
||||
context.appOutDir,
|
||||
`${pkg.name}.app/Contents/Resources/app.asar.unpacked/dist/bin`
|
||||
`${pkg.productName}.app/Contents/Resources/app.asar.unpacked/dist/bin`
|
||||
);
|
||||
|
||||
// Reapply file permissions to the wavesrv binaries in the final app package
|
||||
|
||||
@@ -144,6 +144,17 @@ const QuickTips = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tip-section-header">Need More Help?</div>
|
||||
<div className="tip">
|
||||
<div>
|
||||
<div>
|
||||
<a target="_blank" href="https://discord.gg/XfvZ334gwU" rel="noopener">
|
||||
Join Our Discord
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -149,8 +149,11 @@ class CpuPlotViewModel {
|
||||
if (initialData == null) {
|
||||
return;
|
||||
}
|
||||
const newData = this.getDefaultData();
|
||||
const initialDataItems: DataItem[] = initialData.map(convertWaveEventToDataItem);
|
||||
globalStore.set(this.addDataAtom, initialDataItems);
|
||||
// splice the initial data into the default data (replacing the newest points)
|
||||
newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems);
|
||||
globalStore.set(this.addDataAtom, newData);
|
||||
} catch (e) {
|
||||
console.log("Error loading initial data for cpuplot", e);
|
||||
} finally {
|
||||
@@ -158,7 +161,7 @@ class CpuPlotViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultData(): Array<DataItem> {
|
||||
getDefaultData(): DataItem[] {
|
||||
// set it back one to avoid backwards line being possible
|
||||
const numPoints = globalStore.get(this.numPoints);
|
||||
const currentTime = Date.now() - 1000;
|
||||
@@ -197,6 +200,8 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
|
||||
lastConnName.current = connName;
|
||||
model.loadInitialData();
|
||||
}
|
||||
}, [connStatus.status, connName]);
|
||||
React.useEffect(() => {
|
||||
const unsubFn = waveEventSubscribe({
|
||||
eventType: "sysinfo",
|
||||
scope: connName,
|
||||
@@ -209,11 +214,11 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
|
||||
addPlotData([dataItem]);
|
||||
},
|
||||
});
|
||||
console.log("subscribe to sysinfo", connName);
|
||||
return () => {
|
||||
unsubFn();
|
||||
};
|
||||
}, [connName]);
|
||||
React.useEffect(() => {}, [connName]);
|
||||
if (connStatus?.status != "connected") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -97,54 +97,6 @@ function isStreamingType(mimeType: string): boolean {
|
||||
mimeType.startsWith("image/")
|
||||
);
|
||||
}
|
||||
const previewTipText = `
|
||||
### Preview
|
||||
Preview is the generic type of block used for viewing files. This can take many different forms based on the type of file being viewed.
|
||||
You can use \`wsh view [path]\` from any Wave terminal window to open a preview block with the contents of the specified path (e.g. \`wsh view .\` or \`wsh view ~/myimage.jpg\`).
|
||||
|
||||
#### Directory
|
||||
When looking at a directory, preview will show a file viewer much like MacOS' *Finder* application or Windows' *File Explorer* application. This variant is slightly more geared toward software development with the focus on seeing what is shown by the \`ls -alh\` command.
|
||||
|
||||
##### View a New File
|
||||
The simplest way to view a new file is to double click its row in the file viewer. Alternatively, while the block is focused, you can use the ↑ and ↓ arrow keys to select a row and press enter to preview the associated file.
|
||||
|
||||
##### View the Parent Directory
|
||||
In the directory view, this is as simple as opening the \`..\` file as if it were a regular file. This can be done with the method above. You can also use the keyboard shortcut \`Cmd + ArrowUp\`.
|
||||
|
||||
##### Navigate Back and Forward
|
||||
When looking at a file, you can navigate back by clicking the back button in the block header or the keyboard shortcut \`Cmd + ArrowLeft\`. You can always navigate back and forward using \`Cmd + ArrowLeft\` and \`Cmd + ArrowRight\`.
|
||||
|
||||
##### Filter the List of Files
|
||||
While the block is focused, you can filter by filename by typing a substring of the filename you're working for. To clear the filter, you can click the ✕ on the filter dropdown or press esc.
|
||||
|
||||
##### Sort by a File Column
|
||||
To sort a file by a specific column, click on the header for that column. If you click the header again, it will reverse the sort order.
|
||||
|
||||
##### Hide and Show Hidden Files
|
||||
At the right of the block header, there is an 👁️ button. Clicking this button hides and shows hidden files.
|
||||
|
||||
##### Refresh the Directory
|
||||
At the right of the block header, there is a refresh button. Clicking this button refreshes the directory contents.
|
||||
|
||||
##### Navigate to Common Directories
|
||||
At the left of the block header, there is a file icon. Clicking and holding on this icon opens a menu where you can select a common folder to navigate to. The available options are *Home*, *Desktop*, *Downloads*, and *Root*.
|
||||
|
||||
##### Open a New Terminal in the Current Directory
|
||||
If you right click the header of the block (alternatively, click the gear icon), one of the menu items listed is **Open Terminal in New Block**. This will create a new terminal block at your current directory.
|
||||
|
||||
##### Open a New Terminal in a Child Directory
|
||||
If you want to open a terminal for a child directory instead, you can right click on that file's row to get the **Open Terminal in New Block** option. Clicking this will open a terminal at that directory. Note that this option is only available for children that are directories.
|
||||
|
||||
##### Open a New Preview for a Child
|
||||
To open a new Preview Block for a Child, you can right click on that file's row and select the **Open Preview in New Block** option.
|
||||
|
||||
#### Markdown
|
||||
Opening a markdown file will bring up a view of the rendered markdown. These files cannot be edited in the preview at this time.
|
||||
|
||||
#### Images/Media
|
||||
Opening a picture will bring up the image of that picture. Opening a video will bring up a player that lets you watch the video.
|
||||
`;
|
||||
|
||||
export class PreviewModel implements ViewModel {
|
||||
viewType: string;
|
||||
blockId: string;
|
||||
@@ -173,7 +125,8 @@ export class PreviewModel implements ViewModel {
|
||||
fullFile: jotai.Atom<Promise<FullFile>>;
|
||||
fileMimeType: jotai.Atom<Promise<string>>;
|
||||
fileMimeTypeLoadable: jotai.Atom<Loadable<string>>;
|
||||
fileContent: jotai.Atom<Promise<string>>;
|
||||
fileContentSaved: jotai.PrimitiveAtom<string | null>;
|
||||
fileContent: jotai.WritableAtom<Promise<string>, [string], void>;
|
||||
newFileContent: jotai.PrimitiveAtom<string | null>;
|
||||
|
||||
openFileModal: jotai.PrimitiveAtom<boolean>;
|
||||
@@ -191,6 +144,8 @@ export class PreviewModel implements ViewModel {
|
||||
codeEditKeyDownHandler: (waveEvent: WaveKeyboardEvent) => boolean;
|
||||
|
||||
setPreviewFileName(fileName: string) {
|
||||
globalStore.set(this.fileContentSaved, null);
|
||||
globalStore.set(this.newFileContent, null);
|
||||
services.ObjectService.UpdateObjectMeta(`block:${this.blockId}`, { file: fileName });
|
||||
}
|
||||
|
||||
@@ -430,10 +385,25 @@ export class PreviewModel implements ViewModel {
|
||||
return file;
|
||||
});
|
||||
|
||||
const fileContentAtom = jotai.atom<Promise<string>>(async (get) => {
|
||||
const fullFile = await get(fullFileAtom);
|
||||
return util.base64ToString(fullFile?.data64);
|
||||
});
|
||||
this.fileContentSaved = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
|
||||
const fileContentAtom = jotai.atom(
|
||||
async (get) => {
|
||||
const _ = get(this.metaFilePath);
|
||||
const newContent = get(this.newFileContent);
|
||||
if (newContent != null) {
|
||||
return newContent;
|
||||
}
|
||||
const savedContent = get(this.fileContentSaved);
|
||||
if (savedContent != null) {
|
||||
return savedContent;
|
||||
}
|
||||
const fullFile = await get(fullFileAtom);
|
||||
return util.base64ToString(fullFile?.data64);
|
||||
},
|
||||
(get, set, update: string) => {
|
||||
set(this.fileContentSaved, update);
|
||||
}
|
||||
);
|
||||
|
||||
this.fullFile = fullFileAtom;
|
||||
this.fileContent = fileContentAtom;
|
||||
@@ -590,6 +560,7 @@ export class PreviewModel implements ViewModel {
|
||||
const conn = globalStore.get(this.connection) ?? "";
|
||||
try {
|
||||
services.FileService.SaveFile(conn, filePath, util.stringToBase64(newFileContent));
|
||||
globalStore.set(this.fileContent, newFileContent);
|
||||
globalStore.set(this.newFileContent, null);
|
||||
console.log("saved file", filePath);
|
||||
} catch (error) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.quicktips-view {
|
||||
padding: 10px 2px;
|
||||
padding: 10px 5px;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { getApi, openLink } from "@/app/store/global";
|
||||
import { getApi, openLink, useSettingsKeyAtom } from "@/app/store/global";
|
||||
import { getSimpleControlShiftAtom } from "@/app/store/keymodel";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { WindowRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { NodeModel } from "@/layout/index";
|
||||
import { WOS, globalStore } from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
@@ -30,6 +32,7 @@ export class WebViewModel implements ViewModel {
|
||||
webviewRef: React.RefObject<WebviewTag>;
|
||||
urlInputRef: React.RefObject<HTMLInputElement>;
|
||||
nodeModel: NodeModel;
|
||||
endIconButtons?: jotai.Atom<IconButtonDecl[]>;
|
||||
|
||||
constructor(blockId: string, nodeModel: NodeModel) {
|
||||
this.nodeModel = nodeModel;
|
||||
@@ -48,7 +51,8 @@ export class WebViewModel implements ViewModel {
|
||||
this.webviewRef = React.createRef<WebviewTag>();
|
||||
|
||||
this.viewText = jotai.atom((get) => {
|
||||
let url = get(this.blockAtom)?.meta?.url || "";
|
||||
const defaultUrlAtom = useSettingsKeyAtom("web:defaulturl");
|
||||
let url = get(this.blockAtom)?.meta?.url || get(defaultUrlAtom);
|
||||
const currUrl = get(this.url);
|
||||
if (currUrl !== undefined) {
|
||||
url = currUrl;
|
||||
@@ -91,6 +95,22 @@ export class WebViewModel implements ViewModel {
|
||||
},
|
||||
] as HeaderElem[];
|
||||
});
|
||||
|
||||
this.endIconButtons = jotai.atom((get) => {
|
||||
return [
|
||||
{
|
||||
elemtype: "iconbutton",
|
||||
icon: "arrow-up-right-from-square",
|
||||
title: "Open in External Browser",
|
||||
click: () => {
|
||||
const url = this.getUrl();
|
||||
if (url != null && url != "") {
|
||||
return getApi().openExternal(this.getUrl());
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +219,11 @@ export class WebViewModel implements ViewModel {
|
||||
globalStore.set(this.url, url);
|
||||
}
|
||||
|
||||
ensureUrlScheme(url: string) {
|
||||
ensureUrlScheme(url: string, searchTemplate: string) {
|
||||
if (url == null) {
|
||||
url = "";
|
||||
}
|
||||
|
||||
if (/^(http|https):/.test(url)) {
|
||||
// If the URL starts with http: or https:, return it as is
|
||||
return url;
|
||||
@@ -223,23 +247,10 @@ export class WebViewModel implements ViewModel {
|
||||
}
|
||||
|
||||
// Otherwise, treat it as a search query
|
||||
return `https://www.google.com/search?q=${encodeURIComponent(url)}`;
|
||||
}
|
||||
|
||||
normalizeUrl(url: string) {
|
||||
if (!url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
if (parsedUrl.hostname.startsWith("www.")) {
|
||||
parsedUrl.hostname = parsedUrl.hostname.slice(4);
|
||||
}
|
||||
return parsedUrl.href;
|
||||
} catch (e) {
|
||||
return url.replace(/\/+$/, "") + "/";
|
||||
if (searchTemplate == null) {
|
||||
return `https://www.google.com/search?q=${encodeURIComponent(url)}`;
|
||||
}
|
||||
return searchTemplate.replace("{query}", encodeURIComponent(url));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,7 +258,9 @@ export class WebViewModel implements ViewModel {
|
||||
* @param newUrl The new URL to load in the webview.
|
||||
*/
|
||||
loadUrl(newUrl: string, reason: string) {
|
||||
const nextUrl = this.ensureUrlScheme(newUrl);
|
||||
const defaultSearchAtom = useSettingsKeyAtom("web:defaultsearch");
|
||||
const searchTemplate = globalStore.get(defaultSearchAtom);
|
||||
const nextUrl = this.ensureUrlScheme(newUrl, searchTemplate);
|
||||
console.log("webview loadUrl", reason, nextUrl, "cur=", this.webviewRef?.current.getURL());
|
||||
if (newUrl != nextUrl) {
|
||||
globalStore.set(this.url, nextUrl);
|
||||
@@ -317,8 +330,20 @@ export class WebViewModel implements ViewModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
getSettingsMenuItems() {
|
||||
getSettingsMenuItems(): ContextMenuItem[] {
|
||||
return [
|
||||
{
|
||||
label: "Set Homepage",
|
||||
click: async () => {
|
||||
const url = this.getUrl();
|
||||
if (url != null && url != "") {
|
||||
RpcApi.SetConfigCommand(WindowRpcClient, { "web:defaulturl": url });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
label: this.webviewRef.current?.isDevToolsOpened() ? "Close DevTools" : "Open DevTools",
|
||||
click: async () => {
|
||||
@@ -347,7 +372,12 @@ interface WebViewProps {
|
||||
|
||||
const WebView = memo(({ model }: WebViewProps) => {
|
||||
const blockData = jotai.useAtomValue(model.blockAtom);
|
||||
const metaUrl = blockData?.meta?.url;
|
||||
const defaultUrlAtom = useSettingsKeyAtom("web:defaulturl");
|
||||
const defaultUrl = jotai.useAtomValue(defaultUrlAtom);
|
||||
const defaultSearchAtom = useSettingsKeyAtom("web:defaultsearch");
|
||||
const defaultSearch = jotai.useAtomValue(defaultSearchAtom);
|
||||
let metaUrl = blockData?.meta?.url || defaultUrl;
|
||||
metaUrl = model.ensureUrlScheme(metaUrl, defaultSearch);
|
||||
const metaUrlRef = React.useRef(metaUrl);
|
||||
|
||||
// The initial value of the block metadata URL when the component first renders. Used to set the starting src value for the webview.
|
||||
|
||||
Vendored
+2
@@ -421,6 +421,8 @@ declare global {
|
||||
"editor:stickyscrollenabled"?: boolean;
|
||||
"web:*"?: boolean;
|
||||
"web:openlinksinternally"?: boolean;
|
||||
"web:defaulturl"?: string;
|
||||
"web:defaultsearch"?: string;
|
||||
"blockheader:*"?: boolean;
|
||||
"blockheader:showblockids"?: boolean;
|
||||
"autoupdate:*"?: boolean;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
"productName": "Wave",
|
||||
"description": "Open-Source AI-Native Terminal Built for Seamless Workflows",
|
||||
"license": "Apache-2.0",
|
||||
"version": "0.8.0-beta.7",
|
||||
"version": "0.8.0-beta.14",
|
||||
"homepage": "https://waveterm.dev",
|
||||
"build": {
|
||||
"appId": "dev.commandline.waveterm"
|
||||
|
||||
@@ -166,7 +166,7 @@ func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts Comm
|
||||
} else if remote.IsPowershell(shellPath) {
|
||||
// powershell is weird about quoted path executables and requires an ampersand first
|
||||
shellPath = "& " + shellPath
|
||||
shellOpts = append(shellOpts, "-NoExit", "-File", homeDir+fmt.Sprintf("/.waveterm/%s/wavepwsh.ps1", shellutil.PwshIntegrationDir))
|
||||
shellOpts = append(shellOpts, "-ExecutionPolicy", "Bypass", "-NoExit", "-File", homeDir+fmt.Sprintf("/.waveterm/%s/wavepwsh.ps1", shellutil.PwshIntegrationDir))
|
||||
} else {
|
||||
if cmdOpts.Login {
|
||||
shellOpts = append(shellOpts, "-l")
|
||||
@@ -276,7 +276,7 @@ func StartShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOpt
|
||||
// cant set -l or -i with --rcfile
|
||||
shellOpts = append(shellOpts, "--rcfile", shellutil.GetBashRcFileOverride())
|
||||
} else if remote.IsPowershell(shellPath) {
|
||||
shellOpts = append(shellOpts, "-NoExit", "-File", shellutil.GetWavePowershellEnv())
|
||||
shellOpts = append(shellOpts, "-ExecutionPolicy", "Bypass", "-NoExit", "-File", shellutil.GetWavePowershellEnv())
|
||||
} else {
|
||||
if cmdOpts.Login {
|
||||
shellOpts = append(shellOpts, "-l")
|
||||
|
||||
@@ -629,7 +629,7 @@ func CopyToChannel(outputCh chan<- []byte, reader io.Reader) error {
|
||||
// on error just returns ""
|
||||
// does not return "application/octet-stream" as this is considered a detection failure
|
||||
// can pass an existing fileInfo to avoid re-statting the file
|
||||
func DetectMimeType(path string, fileInfo fs.FileInfo) string {
|
||||
func DetectMimeType(path string, fileInfo fs.FileInfo, extended bool) string {
|
||||
if fileInfo == nil {
|
||||
statRtn, err := os.Stat(path)
|
||||
if err != nil {
|
||||
@@ -657,6 +657,9 @@ func DetectMimeType(path string, fileInfo fs.FileInfo) string {
|
||||
if mimeType := mime.TypeByExtension(ext); mimeType != "" {
|
||||
return mimeType
|
||||
}
|
||||
if !extended {
|
||||
return ""
|
||||
}
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
"label": "web",
|
||||
"blockdef": {
|
||||
"meta": {
|
||||
"view": "web",
|
||||
"url": "https://waveterm.dev/"
|
||||
"view": "web"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
"autoupdate:installonquit": true,
|
||||
"autoupdate:intervalms": 3600000,
|
||||
"editor:minimapenabled": true,
|
||||
"web:defaulturl": "https://github.com/wavetermdev/waveterm",
|
||||
"web:defaultsearch": "https://www.google.com/search?q={query}",
|
||||
"window:tilegapsize": 3,
|
||||
"telemetry:enabled": true
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
|
||||
ConfigKey_WebClear = "web:*"
|
||||
ConfigKey_WebOpenLinksInternally = "web:openlinksinternally"
|
||||
ConfigKey_WebDefaultUrl = "web:defaulturl"
|
||||
ConfigKey_WebDefaultSearch = "web:defaultsearch"
|
||||
|
||||
ConfigKey_BlockHeaderClear = "blockheader:*"
|
||||
ConfigKey_BlockHeaderShowBlockIds = "blockheader:showblockids"
|
||||
|
||||
@@ -54,8 +54,10 @@ type SettingsType struct {
|
||||
EditorMinimapEnabled bool `json:"editor:minimapenabled,omitempty"`
|
||||
EditorStickyScrollEnabled bool `json:"editor:stickyscrollenabled,omitempty"`
|
||||
|
||||
WebClear bool `json:"web:*,omitempty"`
|
||||
WebOpenLinksInternally bool `json:"web:openlinksinternally,omitempty"`
|
||||
WebClear bool `json:"web:*,omitempty"`
|
||||
WebOpenLinksInternally bool `json:"web:openlinksinternally,omitempty"`
|
||||
WebDefaultUrl string `json:"web:defaulturl,omitempty"`
|
||||
WebDefaultSearch string `json:"web:defaultsearch,omitempty"`
|
||||
|
||||
BlockHeaderClear bool `json:"blockheader:*,omitempty"`
|
||||
BlockHeaderShowBlockIds bool `json:"blockheader:showblockids,omitempty"`
|
||||
|
||||
@@ -55,10 +55,15 @@ func GetStarterLayout() PortableLayout {
|
||||
}},
|
||||
{IndexArr: []int{2}, BlockDef: &waveobj.BlockDef{
|
||||
Meta: waveobj.MetaMapType{
|
||||
waveobj.MetaKey_View: "help",
|
||||
waveobj.MetaKey_View: "tips",
|
||||
},
|
||||
}},
|
||||
{IndexArr: []int{2, 1}, BlockDef: &waveobj.BlockDef{
|
||||
Meta: waveobj.MetaMapType{
|
||||
waveobj.MetaKey_View: "help",
|
||||
},
|
||||
}},
|
||||
{IndexArr: []int{2, 2}, BlockDef: &waveobj.BlockDef{
|
||||
Meta: waveobj.MetaMapType{
|
||||
waveobj.MetaKey_View: "waveai",
|
||||
},
|
||||
|
||||
@@ -104,7 +104,7 @@ func (impl *ServerImpl) remoteStreamFileDir(ctx context.Context, path string, by
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
innerFileInfo := statToFileInfo(filepath.Join(path, innerFileInfoInt.Name()), innerFileInfoInt)
|
||||
innerFileInfo := statToFileInfo(filepath.Join(path, innerFileInfoInt.Name()), innerFileInfoInt, false)
|
||||
fileInfoArr = append(fileInfoArr, innerFileInfo)
|
||||
if len(fileInfoArr) >= DirChunkSize {
|
||||
dataCallback(fileInfoArr, nil)
|
||||
@@ -200,8 +200,8 @@ func (impl *ServerImpl) RemoteStreamFileCommand(ctx context.Context, data wshrpc
|
||||
return ch
|
||||
}
|
||||
|
||||
func statToFileInfo(fullPath string, finfo fs.FileInfo) *wshrpc.FileInfo {
|
||||
mimeType := utilfn.DetectMimeType(fullPath, finfo)
|
||||
func statToFileInfo(fullPath string, finfo fs.FileInfo, extended bool) *wshrpc.FileInfo {
|
||||
mimeType := utilfn.DetectMimeType(fullPath, finfo, extended)
|
||||
rtn := &wshrpc.FileInfo{
|
||||
Path: wavebase.ReplaceHomeDir(fullPath),
|
||||
Dir: computeDirPart(fullPath, finfo.IsDir()),
|
||||
@@ -272,7 +272,7 @@ func (*ServerImpl) fileInfoInternal(path string, extended bool) (*wshrpc.FileInf
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot stat file %q: %w", path, err)
|
||||
}
|
||||
rtn := statToFileInfo(cleanedPath, finfo)
|
||||
rtn := statToFileInfo(cleanedPath, finfo, extended)
|
||||
if extended {
|
||||
rtn.ReadOnly = checkIsReadOnly(cleanedPath, finfo, true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user