Workspaces and Terminals
This commit is contained in:
@@ -3,23 +3,26 @@ import { ChevronRight, Home } from 'lucide-react';
|
||||
type BreadcrumbProps = {
|
||||
path: string;
|
||||
onNavigate: (path: string) => void;
|
||||
basePath?: string;
|
||||
};
|
||||
|
||||
export const Breadcrumb = ({ path, onNavigate }: BreadcrumbProps) => {
|
||||
const segments = path.split('/').filter(Boolean);
|
||||
export const Breadcrumb = ({ path, onNavigate, basePath = '/' }: BreadcrumbProps) => {
|
||||
const relativePath = basePath !== '/' && path.startsWith(basePath) ? path.slice(basePath.length) : path;
|
||||
const segments = relativePath.split('/').filter(Boolean);
|
||||
|
||||
return (
|
||||
<nav className="flex items-center gap-1 text-sm flex-wrap">
|
||||
<button
|
||||
onClick={() => onNavigate('/')}
|
||||
onClick={() => onNavigate(basePath)}
|
||||
className="flex items-center gap-1 text-duck-forest hover:text-duck-teal transition-colors cursor-pointer font-medium"
|
||||
>
|
||||
<Home className="h-4 w-4" />
|
||||
<span>home</span>
|
||||
<span>{basePath === '/' ? 'home' : basePath.split('/').pop()}</span>
|
||||
</button>
|
||||
|
||||
{segments.map((segment, i) => {
|
||||
const segmentPath = '/' + segments.slice(0, i + 1).join('/');
|
||||
const relative = '/' + segments.slice(0, i + 1).join('/');
|
||||
const segmentPath = basePath === '/' ? relative : basePath + relative;
|
||||
const isLast = i === segments.length - 1;
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,6 +18,7 @@ export type TerminalViewProps = {
|
||||
wsPath?: string;
|
||||
sessionId?: string;
|
||||
sandboxed?: boolean;
|
||||
cwd?: string;
|
||||
fontSize?: number;
|
||||
fontFamily?: string;
|
||||
theme?: TerminalTheme;
|
||||
@@ -34,13 +35,14 @@ const DEFAULT_THEME: Required<TerminalTheme> = {
|
||||
selectionBackground: '#3a3a5e',
|
||||
};
|
||||
|
||||
const buildWsUrl = (wsPath: string, sessionId?: string, sandboxed?: boolean) => {
|
||||
const buildWsUrl = (wsPath: string, sessionId?: string, sandboxed?: boolean, cwd?: string) => {
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const token = localStorage.getItem('BEARER_TOKEN') ?? '';
|
||||
const separator = wsPath.includes('?') ? '&' : '?';
|
||||
let url = `${protocol}//${window.location.host}${wsPath}${separator}token=${encodeURIComponent(token)}`;
|
||||
if (sessionId) url += `&sessionId=${encodeURIComponent(sessionId)}`;
|
||||
if (sandboxed === false) url += '&sandboxed=false';
|
||||
if (cwd) url += `&cwd=${encodeURIComponent(cwd)}`;
|
||||
return url;
|
||||
};
|
||||
|
||||
@@ -50,6 +52,7 @@ export const TerminalView = ({
|
||||
wsPath = '/api/terminal/ws',
|
||||
sessionId,
|
||||
sandboxed = true,
|
||||
cwd,
|
||||
fontSize = 14,
|
||||
fontFamily = 'Menlo, Monaco, "Courier New", monospace',
|
||||
theme,
|
||||
@@ -99,6 +102,13 @@ export const TerminalView = ({
|
||||
const fitAddon = new FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
term.open(container);
|
||||
|
||||
const viewport = container.querySelector('.xterm-viewport') as HTMLElement | null;
|
||||
if (viewport) {
|
||||
viewport.style.scrollbarWidth = 'none';
|
||||
viewport.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
fitAddon.fit();
|
||||
if (autoFocus) term.focus();
|
||||
|
||||
@@ -106,7 +116,7 @@ export const TerminalView = ({
|
||||
fitAddonRef.current = fitAddon;
|
||||
onReadyRef.current?.(term);
|
||||
|
||||
const ws = new WebSocket(buildWsUrl(wsPath, sessionId, sandboxed));
|
||||
const ws = new WebSocket(buildWsUrl(wsPath, sessionId, sandboxed, cwd));
|
||||
wsRef.current = ws;
|
||||
|
||||
const handleOpen = () => {
|
||||
@@ -180,6 +190,7 @@ export const TerminalView = ({
|
||||
wsPath,
|
||||
sessionId,
|
||||
sandboxed,
|
||||
cwd,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
background,
|
||||
@@ -190,10 +201,8 @@ export const TerminalView = ({
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={className}
|
||||
style={{ backgroundColor: background, ...style }}
|
||||
/>
|
||||
<div className={className} style={{ backgroundColor: background, overflow: 'hidden', ...style }}>
|
||||
<div ref={containerRef} style={{ width: '100%', height: '100%' }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user