feat: add Files tab for browsing workspace files (#154)
* feat: add Files tab for browsing workspace files - Add Files tab between Diff and Terminal in header - Implement hierarchical file tree with expand/collapse - Add fuzzy search with debouncing and relevance ranking - Support gitignore filtering via `git check-ignore` (web + desktop) - Add syntax highlighting for 150+ file types - Add image preview (SVG, PNG, JPG, etc.) - Add line numbers, wrap toggle, and copy button - Desktop: split-pane layout matching DiffView - Mobile: drill-in navigation with full-width sidebar - Update help dialog with Cmd+3 shortcut - Increase header breakpoint to 940px for new tab * feat: enhance context and session stores to track agent/model/variant choices for historical sessions * feat: implement line selection and commenting functionality in FilesView
This commit is contained in:
committed by
GitHub
parent
ecf81c901d
commit
1be5dfda05
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,11 @@ import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
import { ensureFlexokiThemesRegistered } from '@/lib/shiki/registerFlexokiThemes';
|
||||
import { flexokiThemeNames } from '@/lib/shiki/flexokiThemes';
|
||||
|
||||
import { toast } from 'sonner';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useSessionStore } from '@/stores/useSessionStore';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { useContextStore } from '@/stores/contextStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { cn, getModifierLabel } from '@/lib/utils';
|
||||
@@ -158,6 +160,9 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
|
||||
const sendMessage = useSessionStore(state => state.sendMessage);
|
||||
const currentSessionId = useSessionStore(state => state.currentSessionId);
|
||||
const { currentProviderId, currentModelId, currentAgentName, currentVariant } = useConfigStore();
|
||||
const getSessionAgentSelection = useContextStore(state => state.getSessionAgentSelection);
|
||||
const getAgentModelForSession = useContextStore(state => state.getAgentModelForSession);
|
||||
const getAgentModelVariantForSession = useContextStore(state => state.getAgentModelVariantForSession);
|
||||
|
||||
// Update main content center on resize
|
||||
useEffect(() => {
|
||||
@@ -210,6 +215,9 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
|
||||
// Check if click is inside the comment UI portal
|
||||
const commentUI = document.querySelector('[data-comment-ui]');
|
||||
if (commentUI?.contains(target)) return;
|
||||
|
||||
// Check if click is inside toast (sonner)
|
||||
if (target.closest('[data-sonner-toast]') || target.closest('[data-sonner-toaster]')) return;
|
||||
|
||||
// Check if click is on a line number (inside shadow DOM)
|
||||
const path = e.composedPath();
|
||||
@@ -239,10 +247,25 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
|
||||
|
||||
const handleSendComment = useCallback(async () => {
|
||||
if (!selection || !commentText.trim()) return;
|
||||
if (!currentSessionId || !currentProviderId || !currentModelId) {
|
||||
console.warn('Cannot send comment: no active session or model not selected');
|
||||
if (!currentSessionId) {
|
||||
toast.error('Select a session to send comment');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get session-specific agent/model/variant with fallback to config values
|
||||
const sessionAgent = getSessionAgentSelection(currentSessionId) || currentAgentName;
|
||||
const sessionModel = sessionAgent ? getAgentModelForSession(currentSessionId, sessionAgent) : null;
|
||||
const effectiveProviderId = sessionModel?.providerId || currentProviderId;
|
||||
const effectiveModelId = sessionModel?.modelId || currentModelId;
|
||||
|
||||
if (!effectiveProviderId || !effectiveModelId) {
|
||||
toast.error('Select a model to send comment');
|
||||
return;
|
||||
}
|
||||
|
||||
const effectiveVariant = sessionAgent && effectiveProviderId && effectiveModelId
|
||||
? getAgentModelVariantForSession(currentSessionId, sessionAgent, effectiveProviderId, effectiveModelId) ?? currentVariant
|
||||
: currentVariant;
|
||||
|
||||
const code = extractSelectedCode(original, modified, selection);
|
||||
const startLine = selection.start;
|
||||
@@ -259,18 +282,18 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
|
||||
try {
|
||||
await sendMessage(
|
||||
message,
|
||||
currentProviderId,
|
||||
currentModelId,
|
||||
currentAgentName,
|
||||
effectiveProviderId,
|
||||
effectiveModelId,
|
||||
sessionAgent,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
currentVariant
|
||||
effectiveVariant
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to send comment", e);
|
||||
}
|
||||
}, [selection, commentText, original, modified, fileName, language, sendMessage, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, setActiveMainTab]);
|
||||
}, [selection, commentText, original, modified, fileName, language, sendMessage, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, setActiveMainTab, getSessionAgentSelection, getAgentModelForSession, getAgentModelVariantForSession]);
|
||||
|
||||
ensureFlexokiThemesRegistered();
|
||||
|
||||
@@ -339,7 +362,7 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center gap-2 px-4"
|
||||
style={{ width: 'min(100vw - 2rem, 42rem)' }}
|
||||
style={{ width: 'min(100vw - 1rem, 42rem)' }}
|
||||
>
|
||||
<div className="w-full rounded-xl border bg-sidebar flex flex-col relative shadow-lg" style={{ borderColor: 'var(--primary)' }}>
|
||||
{/* Textarea - auto-grows from 1 line to max 5 lines */}
|
||||
|
||||
@@ -45,7 +45,7 @@ const SETTINGS_SIDEBAR_MAX_WIDTH = 500;
|
||||
const SETTINGS_SIDEBAR_DEFAULT_WIDTH = 264;
|
||||
|
||||
// Width threshold for hiding tab labels (show icons only)
|
||||
const TAB_LABELS_MIN_WIDTH = 700;
|
||||
const TAB_LABELS_MIN_WIDTH = 940;
|
||||
|
||||
interface SettingsViewProps {
|
||||
onClose?: () => void;
|
||||
|
||||
@@ -2,4 +2,5 @@ export { ChatView } from './ChatView';
|
||||
export { GitView } from './GitView';
|
||||
export { DiffView, useDiffFileCount } from './DiffView';
|
||||
export { TerminalView } from './TerminalView';
|
||||
export { FilesView } from './FilesView';
|
||||
export { SettingsView } from './SettingsView';
|
||||
|
||||
Reference in New Issue
Block a user