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
8 changes: 8 additions & 0 deletions lib/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
framework: '@storybook/react-vite',
viteFinal: (config) => {
config.resolve ??= {};
config.resolve.alias = {
...(config.resolve.alias as Record<string, string> ?? {}),
'@tauri-apps/api/window': new URL('./tauri-window-mock.ts', import.meta.url).pathname,
};
return config;
},
};

export default config;
11 changes: 11 additions & 0 deletions lib/.storybook/tauri-window-mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const mockWindow = {
isMaximized: () => Promise.resolve(false),
onResized: (_callback: () => void) => Promise.resolve(() => {}),
minimize: () => Promise.resolve(),
toggleMaximize: () => Promise.resolve(),
close: () => Promise.resolve(),
};

export function getCurrentWindow() {
return mockWindow;
}
24 changes: 24 additions & 0 deletions lib/src/components/Pond.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,30 @@ export function Pond({
const handleReattachRef = useRef(handleReattach);
handleReattachRef.current = handleReattach;

// Listen for external "new terminal" requests (e.g. from the standalone AppBar)
useEffect(() => {
const handler = () => {
const api = apiRef.current;
if (!api) return;
const newId = generatePaneId();
const active = api.activePanel;
let direction: 'right' | 'below' = 'right';
if (active) {
direction = (active.api.width - active.api.height > 0) ? 'right' : 'below';
}
api.addPanel({
id: newId,
component: 'terminal',
tabComponent: 'terminal',
title: '<unnamed>',
position: active ? { referencePanel: active.id, direction } : undefined,
});
selectPanel(newId);
};
window.addEventListener('mouseterm:new-terminal', handler);
return () => window.removeEventListener('mouseterm:new-terminal', handler);
}, [generatePaneId, selectPanel]);

const addSplitPanel = useCallback((
id: string | null,
direction: 'right' | 'below',
Expand Down
70 changes: 70 additions & 0 deletions lib/src/stories/AppBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AppBar } from '../../../standalone/src/AppBar';

const DEFAULT_SHELLS = [
{ name: 'bash', path: '/bin/bash' },
{ name: 'zsh', path: '/bin/zsh' },
{ name: 'fish', path: '/usr/bin/fish' },
];

function AppBarStory(props: React.ComponentProps<typeof AppBar>) {
return (
<div style={{ width: '100%' }}>
<AppBar {...props} />
</div>
);
}

const meta: Meta<typeof AppBarStory> = {
title: 'Components/AppBar',
component: AppBarStory,
args: {
projectDir: '/home/user/projects/mouseterm',
homeDir: '/home/user',
shells: DEFAULT_SHELLS,
},
};

export default meta;
type Story = StoryObj<typeof AppBarStory>;

export const Default: Story = {};

export const HomeDirectory: Story = {
args: {
projectDir: '/home/user',
homeDir: '/home/user',
},
};

export const LongPath: Story = {
args: {
projectDir: '/home/user/projects/very-deep/nested/directory/structure/my-project',
homeDir: '/home/user',
},
};

export const SingleShell: Story = {
args: {
shells: [{ name: 'bash', path: '/bin/bash' }],
},
};

export const ManyShells: Story = {
args: {
shells: [
{ name: 'bash', path: '/bin/bash' },
{ name: 'zsh', path: '/bin/zsh' },
{ name: 'fish', path: '/usr/bin/fish' },
{ name: 'sh', path: '/bin/sh' },
{ name: 'nu', path: '/usr/bin/nu' },
],
},
};

export const AbsolutePathOutsideHome: Story = {
args: {
projectDir: '/var/www/my-site',
homeDir: '/home/user',
},
};
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ packages:
- standalone/sidecar
- vscode-ext
- website
minimumReleaseAge: 20160
4 changes: 2 additions & 2 deletions standalone/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions standalone/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ authors = ["DiffPlug"]
license = "FSL-1.1-MIT"
edition = "2021"

[registry]
global-min-publish-age = "14 days"

[lib]
name = "mouseterm_lib"
crate-type = ["lib", "cdylib", "staticlib"]
Expand Down
5 changes: 5 additions & 0 deletions standalone/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"windows": ["main"],
"permissions": [
"core:default",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-is-maximized",
"core:window:allow-start-dragging",
"shell:allow-spawn",
"shell:allow-stdin-write",
"shell:allow-kill",
Expand Down
2 changes: 1 addition & 1 deletion standalone/src-tauri/gen/schemas/acl-manifests.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion standalone/src-tauri/gen/schemas/capabilities.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"default":{"identifier":"default","description":"Default capability set for MouseTerm","local":true,"windows":["main"],"permissions":["core:default","shell:allow-spawn","shell:allow-stdin-write","shell:allow-kill"]}}
{"default":{"identifier":"default","description":"Default capability set for MouseTerm","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-minimize","core:window:allow-toggle-maximize","core:window:allow-close","core:window:allow-is-maximized","core:window:allow-start-dragging","shell:allow-spawn","shell:allow-stdin-write","shell:allow-kill","shell:allow-open"]}}
54 changes: 54 additions & 0 deletions standalone/src-tauri/gen/schemas/desktop-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2419,6 +2419,60 @@
"type": "string",
"const": "shell:deny-stdin-write",
"markdownDescription": "Denies the stdin_write command without any pre-configured scope."
},
{
"description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`",
"type": "string",
"const": "updater:default",
"markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-check",
"markdownDescription": "Enables the check command without any pre-configured scope."
},
{
"description": "Enables the download command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download",
"markdownDescription": "Enables the download command without any pre-configured scope."
},
{
"description": "Enables the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download-and-install",
"markdownDescription": "Enables the download_and_install command without any pre-configured scope."
},
{
"description": "Enables the install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-install",
"markdownDescription": "Enables the install command without any pre-configured scope."
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-check",
"markdownDescription": "Denies the check command without any pre-configured scope."
},
{
"description": "Denies the download command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download",
"markdownDescription": "Denies the download command without any pre-configured scope."
},
{
"description": "Denies the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download-and-install",
"markdownDescription": "Denies the download_and_install command without any pre-configured scope."
},
{
"description": "Denies the install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-install",
"markdownDescription": "Denies the install command without any pre-configured scope."
}
]
},
Expand Down
54 changes: 54 additions & 0 deletions standalone/src-tauri/gen/schemas/macOS-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2419,6 +2419,60 @@
"type": "string",
"const": "shell:deny-stdin-write",
"markdownDescription": "Denies the stdin_write command without any pre-configured scope."
},
{
"description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`",
"type": "string",
"const": "updater:default",
"markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-check",
"markdownDescription": "Enables the check command without any pre-configured scope."
},
{
"description": "Enables the download command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download",
"markdownDescription": "Enables the download command without any pre-configured scope."
},
{
"description": "Enables the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download-and-install",
"markdownDescription": "Enables the download_and_install command without any pre-configured scope."
},
{
"description": "Enables the install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-install",
"markdownDescription": "Enables the install command without any pre-configured scope."
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-check",
"markdownDescription": "Denies the check command without any pre-configured scope."
},
{
"description": "Denies the download command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download",
"markdownDescription": "Denies the download command without any pre-configured scope."
},
{
"description": "Denies the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download-and-install",
"markdownDescription": "Denies the download_and_install command without any pre-configured scope."
},
{
"description": "Denies the install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-install",
"markdownDescription": "Denies the install command without any pre-configured scope."
}
]
},
Expand Down
50 changes: 50 additions & 0 deletions standalone/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,38 @@ fn shutdown_sidecar(state: tauri::State<'_, SidecarState>) {
let _ = state.tx.send(SidecarMsg::Shutdown);
}

#[tauri::command]
fn get_project_dir() -> String {
std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.unwrap_or_default()
}

#[derive(Serialize, Clone)]
struct ShellInfo {
name: String,
path: String,
}

#[tauri::command]
fn get_default_shell() -> ShellInfo {
#[cfg(target_os = "windows")]
let shell_path = std::env::var("ComSpec")
.or_else(|_| std::env::var("COMSPEC"))
.unwrap_or_else(|_| String::from("C:\\Windows\\System32\\cmd.exe"));

#[cfg(not(target_os = "windows"))]
let shell_path = std::env::var("SHELL")
.unwrap_or_else(|_| String::from("/bin/sh"));

let name = Path::new(&shell_path)
.file_name()
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| shell_path.clone());

ShellInfo { name, path: shell_path }
}

fn resolve_sidecar_path(resource_dir: Option<PathBuf>, manifest_dir: &Path) -> PathBuf {
if let Some(ref dir) = resource_dir {
// Tauri maps `../sidecar` to `_up_/sidecar` when bundling resources
Expand Down Expand Up @@ -262,8 +294,24 @@ pub fn run() {
.setup(|app| {
let sidecar_state = start_sidecar(app.handle());
app.manage(sidecar_state);

// On non-macOS, remove native decorations for a fully custom title bar.
// macOS uses titleBarStyle "Overlay" from config instead, which preserves
// rounded corners and native traffic-light buttons.
#[cfg(not(target_os = "macos"))]
{
if let Some(window) = app.get_webview_window("main") {
let _ = window.set_decorations(false);
}
}

Ok(())
})
.on_window_event(|_window, event| {
if let tauri::WindowEvent::CloseRequested { .. } = event {
std::process::exit(0);
}
})
.invoke_handler(tauri::generate_handler![
pty_spawn,
pty_write,
Expand All @@ -273,6 +321,8 @@ pub fn run() {
pty_get_scrollback,
pty_request_init,
shutdown_sidecar,
get_project_dir,
get_default_shell,
])
.run(tauri::generate_context!())
.expect("error while running MouseTerm");
Expand Down
2 changes: 2 additions & 0 deletions standalone/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"windows": [
{
"title": "MouseTerm",
"titleBarStyle": "Overlay",
"hiddenTitle": true,
"width": 1200,
"height": 800,
"minWidth": 800,
Expand Down
Loading
Loading