feat(ui): redesign workspace shell with context panel, tabbed sidebars, and faster diff UX (#433)
* feat: tabbed right sidebar, context panel, floating diff comments * fix: auto-close left sidebar when context panel opens - Increase default context panel width from 520 to 600 pixels - Increase sidebar minimum width from 200 to 300 pixels - Replace collapsible component with custom button in diff view * refactoring: rework sidebars, tabs, and file tree layout - Rewrite AnimatedTabs as segment-style with sliding indicator - Upgrade SidebarFilesTree to match FilesView features (context menus, git status, file icons, CRUD dialogs, fuzzy search ranking) - Restructure FilesView header: tabs row + actions row, remove breadcrumbs - Show relative path in context panel header, track active tab - Allow left sidebar to stay open alongside context panel - Hide diff/files tabs from header on desktop (mobile-only) - Move chevron after group name in session sidebar - Compact tab heights in right sidebar and git view - Size PreviewToggleButton to match other action buttons - Remove directory loading spinner from folder icons * feat: add project icon and color customization - Enable users to assign custom icons to projects - Allow users to choose accent colors for projects - Stabilize repo status UI during project switching * feat: add scroll fade indicators to editor tabs * style: reduce spacing and icon sizes in header * style: adjust tab component padding from uniform to vertical-horizontal * feat: Add session state indicators to project tabs * feat: Enhance session status handling and improve UI responsiveness * fix: preserve upstream tracking on branch rename * fix: improve initial remote selection for pull requests - Uses saved remote name from previous session when available - Selects remote based on tracking branch when possible - Falls back to origin or first available remote * perf(diff): faster highlight, stable stacked scroll - split/unified Pierre worker pools; prefer shiki-wasm - align diff CSS line-height; disable scroll anchoring; drop WebKit compositing hacks - harden stacked pin/align (cancel on user scroll/input); prevent overscroll - make overlay scrollbar MutationObserver optional; disable for diff container * feat: handle binary files in diff view * fix: adjust project tabs layout and drag regions * style: update drag overlay visual styling * feat: enable number keys to switch projects in the sidebar * fix: recognize octet-stream as text-based MIME type * feat: add keyboard navigation to context panel * feat: add session pinning to sidebar - Pin important sessions to keep them at the top - Pinned sessions persist across browser sessions * refactor: move context usage display from chat input to header
This commit is contained in:
committed by
GitHub
parent
12606b9e53
commit
47c943b487
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
import { WorkerPoolContextProvider, useWorkerPool } from '@pierre/diffs/react';
|
||||
import type { SupportedLanguages } from '@pierre/diffs';
|
||||
import { WorkerPoolManager } from '@pierre/diffs/worker';
|
||||
|
||||
import { useOptionalThemeSystem } from './useThemeSystem';
|
||||
import { workerFactory } from '@/lib/diff/workerFactory';
|
||||
@@ -23,30 +23,87 @@ interface DiffWorkerProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
// Component that warms up the worker pool and precomputes diff ASTs
|
||||
type WorkerPoolStyle = 'unified' | 'split';
|
||||
|
||||
const WORKER_POOL_CONFIG: Record<WorkerPoolStyle, { poolSize: number; totalASTLRUCacheSize: number; lineDiffType: 'none' | 'word-alt' }> = {
|
||||
unified: {
|
||||
poolSize: 1,
|
||||
totalASTLRUCacheSize: 24,
|
||||
lineDiffType: 'none',
|
||||
},
|
||||
split: {
|
||||
poolSize: 2,
|
||||
totalASTLRUCacheSize: 56,
|
||||
lineDiffType: 'word-alt',
|
||||
},
|
||||
};
|
||||
|
||||
let unifiedWorkerPool: WorkerPoolManager | undefined;
|
||||
let splitWorkerPool: WorkerPoolManager | undefined;
|
||||
|
||||
const createWorkerPool = (style: WorkerPoolStyle) => {
|
||||
const config = WORKER_POOL_CONFIG[style];
|
||||
const pool = new WorkerPoolManager(
|
||||
{
|
||||
workerFactory,
|
||||
poolSize: config.poolSize,
|
||||
totalASTLRUCacheSize: config.totalASTLRUCacheSize,
|
||||
},
|
||||
{
|
||||
theme: {
|
||||
light: 'pierre-light',
|
||||
dark: 'pierre-dark',
|
||||
},
|
||||
langs: PRELOAD_LANGS,
|
||||
lineDiffType: config.lineDiffType,
|
||||
preferredHighlighter: 'shiki-wasm',
|
||||
}
|
||||
);
|
||||
void pool.initialize();
|
||||
return pool;
|
||||
};
|
||||
|
||||
const getWorkerPool = (style: WorkerPoolStyle): WorkerPoolManager | undefined => {
|
||||
if (typeof window === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (style === 'split') {
|
||||
splitWorkerPool ??= createWorkerPool('split');
|
||||
return splitWorkerPool;
|
||||
}
|
||||
|
||||
unifiedWorkerPool ??= createWorkerPool('unified');
|
||||
return unifiedWorkerPool;
|
||||
};
|
||||
|
||||
const WorkerPoolWarmup: React.FC<{
|
||||
children: React.ReactNode;
|
||||
renderTheme: { light: string; dark: string };
|
||||
}> = ({ children, renderTheme }) => {
|
||||
const workerPool = useWorkerPool();
|
||||
const unifiedPool = useWorkerPool('unified');
|
||||
const splitPool = useWorkerPool('split');
|
||||
|
||||
useEffect(() => {
|
||||
if (!workerPool) {
|
||||
return;
|
||||
if (unifiedPool) {
|
||||
void unifiedPool.setRenderOptions({
|
||||
theme: renderTheme,
|
||||
lineDiffType: WORKER_POOL_CONFIG.unified.lineDiffType,
|
||||
});
|
||||
}
|
||||
|
||||
// Important: WorkerPoolContextProvider uses a singleton and does not react to
|
||||
// prop changes. Update the worker pool render options explicitly.
|
||||
// Force-disable intra-line diff globally (word-level/char-level).
|
||||
void workerPool.setRenderOptions({ theme: renderTheme, lineDiffType: 'none' });
|
||||
}, [renderTheme, workerPool]);
|
||||
if (splitPool) {
|
||||
void splitPool.setRenderOptions({
|
||||
theme: renderTheme,
|
||||
lineDiffType: WORKER_POOL_CONFIG.split.lineDiffType,
|
||||
});
|
||||
}
|
||||
}, [renderTheme, splitPool, unifiedPool]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export const DiffWorkerProvider: React.FC<DiffWorkerProviderProps> = ({ children }) => {
|
||||
const themeSystem = useOptionalThemeSystem();
|
||||
const isDark = themeSystem?.currentTheme?.metadata?.variant === 'dark';
|
||||
|
||||
const fallbackLight = getDefaultTheme(false);
|
||||
const fallbackDark = getDefaultTheme(true);
|
||||
@@ -64,15 +121,6 @@ export const DiffWorkerProvider: React.FC<DiffWorkerProviderProps> = ({ children
|
||||
ensurePierreThemeRegistered(lightTheme);
|
||||
ensurePierreThemeRegistered(darkTheme);
|
||||
|
||||
const highlighterOptions = useMemo(() => ({
|
||||
theme: {
|
||||
dark: darkTheme.metadata.id,
|
||||
light: lightTheme.metadata.id,
|
||||
},
|
||||
themeType: isDark ? ('dark' as const) : ('light' as const),
|
||||
langs: PRELOAD_LANGS,
|
||||
}), [darkTheme.metadata.id, isDark, lightTheme.metadata.id]);
|
||||
|
||||
const renderTheme = useMemo(
|
||||
() => ({
|
||||
light: lightTheme.metadata.id,
|
||||
@@ -82,22 +130,13 @@ export const DiffWorkerProvider: React.FC<DiffWorkerProviderProps> = ({ children
|
||||
);
|
||||
|
||||
return (
|
||||
<WorkerPoolContextProvider
|
||||
poolOptions={{
|
||||
workerFactory,
|
||||
poolSize: 2,
|
||||
totalASTLRUCacheSize: 50,
|
||||
}}
|
||||
highlighterOptions={highlighterOptions}
|
||||
>
|
||||
<WorkerPoolWarmup
|
||||
renderTheme={renderTheme}
|
||||
>
|
||||
{children}
|
||||
</WorkerPoolWarmup>
|
||||
</WorkerPoolContextProvider>
|
||||
<WorkerPoolWarmup renderTheme={renderTheme}>
|
||||
{children}
|
||||
</WorkerPoolWarmup>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { useWorkerPool };
|
||||
export const useWorkerPool = (style: WorkerPoolStyle = 'unified'): WorkerPoolManager | undefined => {
|
||||
return useMemo(() => getWorkerPool(style), [style]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user