mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 470dfb19b7 | |||
| ade5bc01b3 | |||
| c768616a24 | |||
| bb7ea131b4 | |||
| 0b2fdf0ca0 | |||
| eced7120c7 | |||
| 4395c7410d | |||
| b0c5443916 | |||
| 85ec6d45fe | |||
| fd97f895d0 | |||
| 071a051382 | |||
| 9f6e17e728 | |||
| 19e3f123f7 | |||
| 6638dfd67b | |||
| 8fb831fac6 | |||
| 7c2268fb81 | |||
| 466a659ef2 | |||
| 5d62613fb8 | |||
| afc2175bce | |||
| b6e9d2601d | |||
| fb12527e09 |
@@ -1,5 +1,12 @@
|
||||
# Building for release
|
||||
|
||||
## Temporary instructions while waiting on GitHub support
|
||||
|
||||
1. On the `wave8` branch, run `task version -- <none, patch, minor, major> <false, true>` based on the options mentioned in step 2 of the [Step-by-step guide](#step-by-step-guide).
|
||||
2. Commit and push the changes to the `wave8` branch.
|
||||
3. Manually create a tag named `v<version>` attached to `origin/wave8`. Push the tag to GitHub.
|
||||
4. This should kick off the build as normal and create a draft release when done. Continue from step 4 of the [Step-by-step guide](#step-by-step-guide).
|
||||
|
||||
## Step-by-step guide
|
||||
|
||||
1. Go to the [Actions tab](https://github.com/wavetermdev/waveterm/actions) and select "Bump Version" from the left sidebar.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
unregisterBlockComponentModel,
|
||||
} from "@/store/global";
|
||||
import * as WOS from "@/store/wos";
|
||||
import { getElemAsStr } from "@/util/focusutil";
|
||||
import { focusedBlockId, getElemAsStr } from "@/util/focusutil";
|
||||
import * as util from "@/util/util";
|
||||
import { CpuPlotView, CpuPlotViewModel, makeCpuPlotViewModel } from "@/view/cpuplot/cpuplot";
|
||||
import { HelpView, HelpViewModel, makeHelpViewModel } from "@/view/helpview/helpview";
|
||||
@@ -159,12 +159,11 @@ const BlockFull = React.memo(({ nodeModel, viewModel }: FullBlockProps) => {
|
||||
return;
|
||||
}
|
||||
setBlockClicked(false);
|
||||
const focusWithin = blockRef.current?.contains(document.activeElement);
|
||||
const focusWithin = focusedBlockId() == nodeModel.blockId;
|
||||
if (!focusWithin) {
|
||||
setFocusTarget();
|
||||
}
|
||||
if (!isFocused) {
|
||||
console.log("blockClicked focus", nodeModel.blockId);
|
||||
nodeModel.focusNode();
|
||||
}
|
||||
}, [blockClicked, isFocused]);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -342,18 +342,24 @@ const ChatWindow = memo(
|
||||
|
||||
const handleScrollbarInitialized = (instance: OverlayScrollbars) => {
|
||||
const { viewport } = instance.elements();
|
||||
viewport.removeAttribute("tabindex");
|
||||
viewport.scrollTo({
|
||||
behavior: "auto",
|
||||
top: chatWindowRef.current?.scrollHeight || 0,
|
||||
});
|
||||
};
|
||||
|
||||
const handleScrollbarUpdated = (instance: OverlayScrollbars) => {
|
||||
const { viewport } = instance.elements();
|
||||
viewport.removeAttribute("tabindex");
|
||||
};
|
||||
|
||||
return (
|
||||
<OverlayScrollbarsComponent
|
||||
ref={osRef}
|
||||
className="scrollable"
|
||||
options={{ scrollbars: { autoHide: "leave" } }}
|
||||
events={{ initialized: handleScrollbarInitialized }}
|
||||
events={{ initialized: handleScrollbarInitialized, updated: handleScrollbarUpdated }}
|
||||
>
|
||||
<div ref={chatWindowRef} className="chat-window" style={msgWidths}>
|
||||
<div className="filler"></div>
|
||||
@@ -491,22 +497,6 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
|
||||
setSelectedBlockIdx(null);
|
||||
}, [messages, value]);
|
||||
|
||||
const handleContainerClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
inputRef.current?.focus();
|
||||
|
||||
const target = event.target as HTMLElement;
|
||||
if (
|
||||
target.closest(".copy-button") ||
|
||||
target.closest(".fa-square-terminal") ||
|
||||
target.closest(".waveai-input")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pre = target.closest("pre");
|
||||
updatePreTagOutline(pre);
|
||||
};
|
||||
|
||||
const updateScrollTop = () => {
|
||||
const pres = chatWindowRef.current?.querySelectorAll("pre");
|
||||
if (!pres || selectedBlockIdx === null) return;
|
||||
@@ -610,7 +600,7 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
|
||||
}, [locked, handleEnterKeyPressed]);
|
||||
|
||||
return (
|
||||
<div ref={waveaiRef} className="waveai" onClick={handleContainerClick}>
|
||||
<div ref={waveaiRef} className="waveai">
|
||||
<ChatWindow ref={osRef} chatWindowRef={chatWindowRef} messages={messages} msgWidths={msgWidths} />
|
||||
<div className="waveai-controls">
|
||||
<div className="waveai-input-wrapper">
|
||||
|
||||
@@ -302,7 +302,7 @@ export class LayoutModel {
|
||||
* Perform an action against the layout tree state.
|
||||
* @param action The action to perform.
|
||||
*/
|
||||
treeReducer(action: LayoutTreeAction) {
|
||||
treeReducer(action: LayoutTreeAction, setState = true) {
|
||||
switch (action.type) {
|
||||
case LayoutTreeActionType.ComputeMove:
|
||||
this.setter(
|
||||
@@ -369,7 +369,7 @@ export class LayoutModel {
|
||||
this.magnifiedNodeId = this.treeState.magnifiedNodeId;
|
||||
}
|
||||
this.updateTree();
|
||||
this.setTreeStateAtom(true);
|
||||
if (setState) this.setTreeStateAtom(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ export class LayoutModel {
|
||||
* Callback that is invoked when the upstream tree state has been updated. This ensures the model is updated if the atom is not fully loaded when the model is first instantiated.
|
||||
* @param force Whether to force the local tree state to update, regardless of whether the state is already up to date.
|
||||
*/
|
||||
onTreeStateAtomUpdated = debounce(50, async (force = false) => {
|
||||
async onTreeStateAtomUpdated(force = false) {
|
||||
const treeState = this.getter(this.treeStateAtom);
|
||||
// Only update the local tree state if it is different from the one in the upstream atom. This function is called even when the update was initiated by the LayoutModel, so we need to filter out false positives or we'll enter an infinite loop.
|
||||
if (
|
||||
@@ -403,7 +403,7 @@ export class LayoutModel {
|
||||
magnified: action.magnified,
|
||||
focused: action.focused,
|
||||
};
|
||||
this.treeReducer(insertNodeAction);
|
||||
this.treeReducer(insertNodeAction, false);
|
||||
break;
|
||||
}
|
||||
case LayoutTreeActionType.DeleteNode: {
|
||||
@@ -434,13 +434,16 @@ export class LayoutModel {
|
||||
magnified: action.magnified,
|
||||
focused: action.focused,
|
||||
};
|
||||
this.treeReducer(insertAction);
|
||||
this.treeReducer(insertAction, false);
|
||||
break;
|
||||
}
|
||||
case LayoutTreeActionType.ClearTree: {
|
||||
this.treeReducer({
|
||||
type: LayoutTreeActionType.ClearTree,
|
||||
} as LayoutTreeClearTreeAction);
|
||||
this.treeReducer(
|
||||
{
|
||||
type: LayoutTreeActionType.ClearTree,
|
||||
} as LayoutTreeClearTreeAction,
|
||||
false
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -448,12 +451,13 @@ export class LayoutModel {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setTreeStateAtom(true);
|
||||
} else {
|
||||
this.updateTree();
|
||||
this.setTreeStateAtom(force);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upstream tree state atom to the value of the local tree state.
|
||||
|
||||
@@ -25,7 +25,7 @@ export function getLayoutModelForTab(tabAtom: Atom<Tab>): LayoutModel {
|
||||
}
|
||||
const layoutTreeStateAtom = withLayoutTreeStateAtomFromTab(tabAtom);
|
||||
const layoutModel = new LayoutModel(layoutTreeStateAtom, globalStore.get, globalStore.set);
|
||||
globalStore.sub(layoutTreeStateAtom, () => fireAndForget(() => layoutModel.onTreeStateAtomUpdated()));
|
||||
globalStore.sub(layoutTreeStateAtom, () => fireAndForget(async () => layoutModel.onTreeStateAtomUpdated()));
|
||||
layoutModelMap.set(tabId, layoutModel);
|
||||
return layoutModel;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ export function useTileLayout(tabAtom: Atom<Tab>, tileContent: TileLayoutContent
|
||||
useResizeObserver(layoutModel?.displayContainerRef, layoutModel?.onContainerResize);
|
||||
|
||||
// Once the TileLayout is mounted, re-run the state update to get all the nodes to flow in the layout.
|
||||
useLayoutEffect(() => fireAndForget(() => layoutModel.onTreeStateAtomUpdated(true)), []);
|
||||
useEffect(() => fireAndForget(async () => layoutModel.onTreeStateAtomUpdated(true)), []);
|
||||
|
||||
useEffect(() => layoutModel.registerTileLayout(tileContent), [tileContent]);
|
||||
return layoutModel;
|
||||
|
||||
@@ -54,7 +54,7 @@ export function focusedBlockId(): string {
|
||||
}
|
||||
}
|
||||
const sel = document.getSelection();
|
||||
if (sel && sel.anchorNode) {
|
||||
if (sel && sel.anchorNode && sel.rangeCount > 0 && !sel.isCollapsed) {
|
||||
let anchor = sel.anchorNode;
|
||||
if (anchor instanceof Text) {
|
||||
anchor = anchor.parentElement;
|
||||
|
||||
+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.3",
|
||||
"version": "0.8.0-beta.13",
|
||||
"homepage": "https://waveterm.dev",
|
||||
"build": {
|
||||
"appId": "dev.commandline.waveterm"
|
||||
|
||||
Reference in New Issue
Block a user