Skip to content

Commit bf22628

Browse files
committed
feat: word wrap toggle and enhanced file tooltips
- New 'Code Display' settings section with word wrap toggle - Code pre blocks now respect word wrap setting (off by default = horizontal scroll) - File tree nodes show 'X 行 · Y 字符' in tooltip on hover - wordWrap persisted via usePersistentState
1 parent 0bad186 commit bf22628

6 files changed

Lines changed: 44 additions & 6 deletions

File tree

structure-insight/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ const App: React.FC = () => {
118118
onToggleShowCharCount={() => settings.setShowCharCount(!state.showCharCount)}
119119
maxCharsThreshold={state.maxCharsThreshold}
120120
onSetMaxCharsThreshold={settings.setMaxCharsThreshold}
121+
wordWrap={state.wordWrap}
122+
onToggleWordWrap={() => settings.setWordWrap(!state.wordWrap)}
121123
/>
122124
)}
123125
</AnimatePresence>

structure-insight/components/CodeView.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface FileCardProps {
2222
searchOptions: SearchOptions;
2323
activeMatchIndexInFile: number | null;
2424
onCopyPath: (path: string) => void;
25+
wordWrap?: boolean;
2526
}
2627

2728
const IconButton: React.FC<{icon: string, title: string, onClick: () => void, disabled?: boolean, text?: string}> = ({icon, title, onClick, disabled, text}) => (
@@ -49,7 +50,7 @@ function buildSearchRegex(query: string, options: SearchOptions): RegExp | null
4950
const FileCard: React.FC<FileCardProps> = ({
5051
file, isEditing, onStartEdit, onSaveEdit, onCancelEdit,
5152
isMarkdown, isMarkdownPreview, onToggleMarkdownPreview, onShowToast, fontSize,
52-
searchQuery, searchOptions, activeMatchIndexInFile, onCopyPath
53+
searchQuery, searchOptions, activeMatchIndexInFile, onCopyPath, wordWrap
5354
}) => {
5455
const [editText, setEditText] = React.useState(file.content);
5556
const codeRef = React.useRef<HTMLElement>(null);
@@ -211,7 +212,7 @@ const FileCard: React.FC<FileCardProps> = ({
211212
{lineNumbers}
212213
</pre>
213214
<div className="relative flex-1">
214-
<pre className="py-3 pr-3 whitespace-pre-wrap break-words bg-light-bg dark:bg-dark-bg"><code
215+
<pre className={`py-3 pr-3 bg-light-bg dark:bg-dark-bg ${wordWrap ? 'whitespace-pre-wrap break-words' : 'whitespace-pre overflow-x-auto'}`}><code
215216
ref={codeRef}
216217
className={`language-${file.language} hljs`}
217218
/></pre>
@@ -237,13 +238,14 @@ interface CodeViewProps {
237238
searchOptions: SearchOptions;
238239
activeMatchIndexInFile: number | null;
239240
onCopyPath: (path: string) => void;
241+
wordWrap?: boolean;
240242
}
241243

242244
const CodeView: React.FC<CodeViewProps> = (props) => {
243245
const {
244246
selectedFile, editingPath, onStartEdit, onSaveEdit, onCancelEdit,
245247
markdownPreviewPaths, onToggleMarkdownPreview, onShowToast, fontSize,
246-
searchQuery, searchOptions, activeMatchIndexInFile, onCopyPath
248+
searchQuery, searchOptions, activeMatchIndexInFile, onCopyPath, wordWrap
247249
} = props;
248250

249251
if (!selectedFile) {
@@ -278,6 +280,7 @@ const CodeView: React.FC<CodeViewProps> = (props) => {
278280
searchOptions={searchOptions}
279281
activeMatchIndexInFile={activeMatchIndexInFile}
280282
onCopyPath={onCopyPath}
283+
wordWrap={wordWrap}
281284
/>
282285
</motion.div>
283286
</div>

structure-insight/components/FileTree.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ const FileTreeNode: React.FC<{
190190
let displayName = node.name;
191191
const isSelected = !node.isDirectory && node.path === selectedFilePath;
192192

193+
// Add stats to tooltip for processed files
194+
if (!node.isDirectory && (node.status === 'processed' || !node.status) && node.lines && node.chars) {
195+
title = `${node.path}\n${node.lines} 行 · ${node.chars} 字符`;
196+
}
197+
193198

194199
if (node.status === 'skipped') {
195200
statusClass += ' opacity-60';

structure-insight/components/MainContent.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ const MainContent: React.FC<MainContentProps> = ({ logic, codeViewRef, leftPanel
133133
onFileSelect={handlers.handleFileTreeSelect}
134134
onDeleteFile={handlers.handleDeleteFile}
135135
onCopyPath={handlers.handleCopyPath}
136+
wordWrap={state.wordWrap}
136137
onToggleExclude={handlers.handleToggleExclude}
137138
selectedFilePath={state.selectedFilePath}
138139
showCharCount={state.showCharCount}
@@ -174,6 +175,7 @@ const MainContent: React.FC<MainContentProps> = ({ logic, codeViewRef, leftPanel
174175
searchOptions={state.searchOptions}
175176
activeMatchIndexInFile={state.activeMatchIndexInFile}
176177
onCopyPath={handlers.handleCopyPath}
178+
wordWrap={state.wordWrap}
177179
/>
178180
)}
179181
</div>
@@ -212,6 +214,7 @@ const MainContent: React.FC<MainContentProps> = ({ logic, codeViewRef, leftPanel
212214
onFileSelect={handlers.handleFileTreeSelect}
213215
onDeleteFile={handlers.handleDeleteFile}
214216
onCopyPath={handlers.handleCopyPath}
217+
wordWrap={state.wordWrap}
215218
onToggleExclude={handlers.handleToggleExclude}
216219
selectedFilePath={state.selectedFilePath}
217220
showCharCount={state.showCharCount}
@@ -254,6 +257,7 @@ const MainContent: React.FC<MainContentProps> = ({ logic, codeViewRef, leftPanel
254257
searchOptions={state.searchOptions}
255258
activeMatchIndexInFile={state.activeMatchIndexInFile}
256259
onCopyPath={handlers.handleCopyPath}
260+
wordWrap={state.wordWrap}
257261
/>
258262
</div>
259263
<div className={state.activeView === 'structure' ? 'block min-h-full' : 'hidden'}>

structure-insight/components/SettingsDialog.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ interface SettingsDialogProps {
1616
onToggleShowCharCount: () => void;
1717
maxCharsThreshold: number;
1818
onSetMaxCharsThreshold: (val: number) => void;
19+
wordWrap: boolean;
20+
onToggleWordWrap: () => void;
1921
}
2022

2123
const SettingsDialog: React.FC<SettingsDialogProps> = ({
2224
isOpen, onClose, isDarkTheme, onToggleTheme, extractContent, onToggleExtractContent, fontSize, onSetFontSize, onClearCache,
23-
showCharCount, onToggleShowCharCount, maxCharsThreshold, onSetMaxCharsThreshold
25+
showCharCount, onToggleShowCharCount, maxCharsThreshold, onSetMaxCharsThreshold, wordWrap, onToggleWordWrap
2426
}) => {
2527
const dialogRef = React.useRef<HTMLDivElement>(null);
2628
const [stars, setStars] = React.useState<number | null>(null);
@@ -160,6 +162,27 @@ const SettingsDialog: React.FC<SettingsDialogProps> = ({
160162
</div>
161163
</div>
162164

165+
{/* Code Display Section */}
166+
<div>
167+
<SectionTitle>代码显示</SectionTitle>
168+
<div className="space-y-4">
169+
<div className="flex items-center justify-between p-2 rounded-lg hover:bg-light-bg dark:hover:bg-dark-bg/50 transition-colors -mx-2">
170+
<div className="flex items-center space-x-3">
171+
<div className="w-9 h-9 rounded-full bg-indigo-100 dark:bg-indigo-900/30 flex items-center justify-center text-indigo-600 dark:text-indigo-400 shadow-sm">
172+
<i className="fa-solid fa-text-width"></i>
173+
</div>
174+
<div className="flex flex-col">
175+
<label htmlFor="word-wrap-toggle" className="text-sm font-medium text-light-text dark:text-dark-text cursor-pointer select-none">
176+
自动换行
177+
</label>
178+
<span className="text-xs text-light-subtle-text dark:text-dark-subtle-text">代码超出宽度时自动换行</span>
179+
</div>
180+
</div>
181+
<Switch id="word-wrap-toggle" checked={wordWrap} onChange={onToggleWordWrap} />
182+
</div>
183+
</div>
184+
</div>
185+
163186
{/* General Section */}
164187
<div>
165188
<SectionTitle>通用</SectionTitle>

structure-insight/hooks/useAppLogic.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const useAppLogic = (
3030
const [extractContent, setExtractContent] = usePersistentState('extractContent', true);
3131
const [fontSize, setFontSize] = usePersistentState('fontSize', 14);
3232
const [showCharCount, setShowCharCount] = usePersistentState('showCharCount', false);
33+
const [wordWrap, setWordWrap] = usePersistentState('wordWrap', false);
3334
const [maxCharsThreshold, setMaxCharsThreshold] = usePersistentState('maxCharsThreshold', 1000000);
3435

3536
// --- Core Data & Selection State ---
@@ -258,7 +259,7 @@ export const useAppLogic = (
258259
state: {
259260
processedData, isLoading, isDragging, progressMessage, isSettingsOpen, toastMessage,
260261
editingPath, markdownPreviewPaths, confirmation,
261-
isDark, panelWidth, extractContent, fontSize, showCharCount, maxCharsThreshold,
262+
isDark, panelWidth, extractContent, fontSize, showCharCount, maxCharsThreshold, wordWrap,
262263
lastProcessedFiles, mobileView, stats,
263264
isSearchOpen, isFileRankOpen, isShortcutsOpen, searchResults, activeResultIndex, isMobile, isAiChatOpen,
264265
selectedFilePath, selectedFile, activeView,
@@ -279,7 +280,7 @@ export const useAppLogic = (
279280
handleToggleExclude,
280281
},
281282
settings: {
282-
setIsDark, setExtractContent, setFontSize, handleClearCache, setShowCharCount, setMaxCharsThreshold
283+
setIsDark, setExtractContent, setFontSize, handleClearCache, setShowCharCount, setMaxCharsThreshold, setWordWrap
283284
},
284285
};
285286
};

0 commit comments

Comments
 (0)