dsh-explorer 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,94 @@
1
1
  {
2
2
  "name": "dsh-explorer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Native workspace file explorer for DeepSeek Harness with precision drag-and-drop chat insertion, motion collapse, git status badges, compact folders, and tab integration",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./client": {
14
+ "default": "./lib/client.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "dsh": {
22
+ "bundle": {
23
+ "patch": "./cordis.patch.yml"
24
+ },
25
+ "client": {
26
+ "inject": [
27
+ "@deepseek-ai/dsh-client-locale",
28
+ "@deepseek-ai/dsh-client-ui-slots",
29
+ "@deepseek-ai/dsh-client-ui-conversation",
30
+ "@deepseek-ai/dsh-client-ui-sidebar-right",
31
+ "@deepseek-ai/dsh-client-modules"
32
+ ],
33
+ "platform": "web"
34
+ },
35
+ "manifestVersion": 1
36
+ },
37
+ "files": [
38
+ "lib/index.js",
39
+ "lib/client.js",
40
+ "src",
41
+ "cordis.patch.yml",
42
+ "icon.svg",
43
+ "README.md",
44
+ "package.json",
45
+ "LICENSE"
46
+ ],
47
+ "license": "MIT",
48
+ "keywords": [
49
+ "dsh-plugin",
50
+ "deepseek-harness",
51
+ "file-explorer",
52
+ "workspace-explorer"
53
+ ],
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/wwwroot/dsh-explorer.git"
57
+ },
58
+ "dependencies": {
59
+ "clsx": "^2.1.1",
60
+ "ws": "^8.18.0",
61
+ "yaml": "^2.7.0"
62
+ },
63
+ "peerDependencies": {
64
+ "@deepseek-ai/cordis": "^4.0.5",
65
+ "@deepseek-ai/dsh-client-locale": "^0.2.1-rc.1",
66
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.2.1-rc.1",
67
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.2.1-rc.1",
68
+ "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.2.1-rc.1",
69
+ "@deepseek-ai/dsh-client-ui-slots": "^0.2.1-rc.1",
70
+ "@deepseek-ai/dsh-host-webserver": "^0.2.1-rc.1",
71
+ "@deepseek-ai/dsh-session": "^0.2.1-rc.1",
72
+ "@deepseek-ai/schemastery": "^3.18.5",
73
+ "react": "^18.2.0",
74
+ "react-dom": "^18.2.0"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "@deepseek-ai/dsh-client-ui-sidebar-right": {
78
+ "optional": true
79
+ }
80
+ },
81
+ "devDependencies": {
82
+ "@types/node": "^24.0.0",
83
+ "@types/react": "~18.3.1",
84
+ "@types/react-dom": "~18.3.1",
85
+ "@types/ws": "^8.5.10",
86
+ "esbuild": "^0.28.2",
87
+ "typescript": "^5.6.0"
88
+ },
89
+ "scripts": {
90
+ "build": "node scripts/build.mjs",
91
+ "typecheck": "tsc --noEmit"
92
+ }
93
+ }
94
+
@@ -0,0 +1,159 @@
1
+ import React, { useEffect, useRef } from 'react'
2
+ import css from './explorer.module.css'
3
+ import type { FsEntry } from './api.ts'
4
+
5
+ interface ContextMenuProps {
6
+ x: number
7
+ y: number
8
+ entry: FsEntry
9
+ relativePath: string
10
+ onClose: () => void
11
+ onOpenFile?: () => void
12
+ onInsertReference: () => void
13
+ onInsertSnippet: () => void
14
+ onCopyPath: () => void
15
+ onCopyRelativePath: () => void
16
+ onNewFile: () => void
17
+ onNewFolder: () => void
18
+ onRename: () => void
19
+ onDelete: () => void
20
+ }
21
+
22
+ export const ExplorerContextMenu: React.FC<ContextMenuProps> = ({
23
+ x,
24
+ y,
25
+ entry,
26
+ relativePath,
27
+ onClose,
28
+ onOpenFile,
29
+ onInsertReference,
30
+ onInsertSnippet,
31
+ onCopyPath,
32
+ onCopyRelativePath,
33
+ onNewFile,
34
+ onNewFolder,
35
+ onRename,
36
+ onDelete,
37
+ }) => {
38
+ const menuRef = useRef<HTMLDivElement>(null)
39
+
40
+ useEffect(() => {
41
+ const handleOutsideClick = (e: MouseEvent) => {
42
+ if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
43
+ onClose()
44
+ }
45
+ }
46
+ window.addEventListener('mousedown', handleOutsideClick)
47
+ return () => window.removeEventListener('mousedown', handleOutsideClick)
48
+ }, [onClose])
49
+
50
+ // Adjust menu bounds if overflowing screen
51
+ const top = Math.min(y, window.innerHeight - 260)
52
+ const left = Math.min(x, window.innerWidth - 200)
53
+
54
+ return (
55
+ <div ref={menuRef} className={css.contextMenu} style={{ top, left }}>
56
+ {!entry.isDir && onOpenFile && (
57
+ <>
58
+ <div
59
+ className={css.menuItem}
60
+ onClick={() => {
61
+ onOpenFile()
62
+ onClose()
63
+ }}
64
+ >
65
+ <span>Open in New Tab</span>
66
+ <span className={css.menuShortcut}>Double-Click</span>
67
+ </div>
68
+ <div className={css.menuDivider} />
69
+ </>
70
+ )}
71
+ <div
72
+ className={css.menuItem}
73
+ onClick={() => {
74
+ onInsertReference()
75
+ onClose()
76
+ }}
77
+ >
78
+ <span>Insert Mention to Chat</span>
79
+ <span className={css.menuShortcut}>@</span>
80
+ </div>
81
+ {!entry.isDir && (
82
+ <div
83
+ className={css.menuItem}
84
+ onClick={() => {
85
+ onInsertSnippet()
86
+ onClose()
87
+ }}
88
+ >
89
+ <span>Insert Code to Chat</span>
90
+ <span className={css.menuShortcut}>Alt+Drop</span>
91
+ </div>
92
+ )}
93
+ <div className={css.menuDivider} />
94
+ {entry.isDir && (
95
+ <>
96
+ <div
97
+ className={css.menuItem}
98
+ onClick={() => {
99
+ onNewFile()
100
+ onClose()
101
+ }}
102
+ >
103
+ <span>New File...</span>
104
+ </div>
105
+ <div
106
+ className={css.menuItem}
107
+ onClick={() => {
108
+ onNewFolder()
109
+ onClose()
110
+ }}
111
+ >
112
+ <span>New Folder...</span>
113
+ </div>
114
+ <div className={css.menuDivider} />
115
+ </>
116
+ )}
117
+ <div
118
+ className={css.menuItem}
119
+ onClick={() => {
120
+ onCopyRelativePath()
121
+ onClose()
122
+ }}
123
+ >
124
+ <span>Copy Relative Path</span>
125
+ </div>
126
+ <div
127
+ className={css.menuItem}
128
+ onClick={() => {
129
+ onCopyPath()
130
+ onClose()
131
+ }}
132
+ >
133
+ <span>Copy Path</span>
134
+ </div>
135
+ <div className={css.menuDivider} />
136
+ <div
137
+ className={css.menuItem}
138
+ onClick={() => {
139
+ onRename()
140
+ onClose()
141
+ }}
142
+ >
143
+ <span>Rename...</span>
144
+ <span className={css.menuShortcut}>F2</span>
145
+ </div>
146
+ <div
147
+ className={css.menuItem}
148
+ onClick={() => {
149
+ onDelete()
150
+ onClose()
151
+ }}
152
+ >
153
+ <span style={{ color: '#f44336' }}>Delete</span>
154
+ <span className={css.menuShortcut}>Del</span>
155
+ </div>
156
+ </div>
157
+ )
158
+ }
159
+
@@ -0,0 +1,75 @@
1
+ import React from 'react'
2
+ import css from './explorer.module.css'
3
+
4
+ interface ExplorerHeaderProps {
5
+ title: string
6
+ searchOpen: boolean
7
+ optionsOpen: boolean
8
+ onToggleSearch: () => void
9
+ onToggleOptions: () => void
10
+ onNewFile: () => void
11
+ onNewFolder: () => void
12
+ onRefresh: () => void
13
+ onCollapseAll: () => void
14
+ }
15
+
16
+ export const ExplorerHeader: React.FC<ExplorerHeaderProps> = ({
17
+ title,
18
+ searchOpen,
19
+ optionsOpen,
20
+ onToggleSearch,
21
+ onToggleOptions,
22
+ onNewFile,
23
+ onNewFolder,
24
+ onRefresh,
25
+ onCollapseAll,
26
+ }) => {
27
+ return (
28
+ <div className={css.header}>
29
+ <span className={css.headerTitle} title={title}>
30
+ {title}
31
+ </span>
32
+ <div className={css.headerActions}>
33
+ <button
34
+ className={`${css.iconBtn} ${searchOpen ? css.iconBtnActive : ''}`}
35
+ title="Filter files"
36
+ onClick={onToggleSearch}
37
+ >
38
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
39
+ <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
40
+ </svg>
41
+ </button>
42
+ <button className={css.iconBtn} title="New File" onClick={onNewFile}>
43
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
44
+ <path d="M9 1H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V6L9 1zm0 1.5L12.5 6H9V2.5zM8 8.5V11h1V8.5h2.5v-1H9V5H8v2.5H5.5v1H8z"/>
45
+ </svg>
46
+ </button>
47
+ <button className={css.iconBtn} title="New Folder" onClick={onNewFolder}>
48
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
49
+ <path d="M7 2l2 2h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h5zm2.5 6.5H8V6H7v2.5H4.5v1H7V12h1V9.5h2.5v-1z"/>
50
+ </svg>
51
+ </button>
52
+ <button className={css.iconBtn} title="Refresh" onClick={onRefresh}>
53
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
54
+ <path d="M13.65 2.35A7.958 7.958 0 0 0 8 0a8 8 0 1 0 8 8h-2a6 6 0 1 1-1.76-4.24l-2.24 2.24H16V0l-2.35 2.35z"/>
55
+ </svg>
56
+ </button>
57
+ <button className={css.iconBtn} title="Collapse All Folders" onClick={onCollapseAll}>
58
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
59
+ <path d="M1 3.5l1.5-1.5L8 7.5 13.5 2 15 3.5 8 10.5 1 3.5zM1 9.5l1.5-1.5L8 13.5 13.5 8 15 9.5 8 16.5 1 9.5z"/>
60
+ </svg>
61
+ </button>
62
+ <button
63
+ className={`${css.iconBtn} ${optionsOpen ? css.iconBtnActive : ''}`}
64
+ title="Explorer Options"
65
+ onClick={onToggleOptions}
66
+ >
67
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
68
+ <path d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872l-.1-.34zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z"/>
69
+ </svg>
70
+ </button>
71
+ </div>
72
+ </div>
73
+ )
74
+ }
75
+
@@ -0,0 +1,243 @@
1
+ import React, { useEffect, useRef } from 'react'
2
+ import css from './explorer.module.css'
3
+ import type { ExplorerOptions, ExplorerDensity, SingleClickAction } from './options.ts'
4
+
5
+ interface ExplorerOptionsModalProps {
6
+ options: ExplorerOptions
7
+ onChange: (updated: Partial<ExplorerOptions>) => void
8
+ onReset: () => void
9
+ onClose: () => void
10
+ }
11
+
12
+ export const ExplorerOptionsModal: React.FC<ExplorerOptionsModalProps> = ({
13
+ options,
14
+ onChange,
15
+ onReset,
16
+ onClose,
17
+ }) => {
18
+ const popoverRef = useRef<HTMLDivElement>(null)
19
+
20
+ // Close on Click Outside & Escape key
21
+ useEffect(() => {
22
+ const handleMouseDown = (e: MouseEvent) => {
23
+ if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
24
+ onClose()
25
+ }
26
+ }
27
+ const handleKeyDown = (e: KeyboardEvent) => {
28
+ if (e.key === 'Escape') {
29
+ onClose()
30
+ }
31
+ }
32
+
33
+ document.addEventListener('mousedown', handleMouseDown, true)
34
+ document.addEventListener('keydown', handleKeyDown, true)
35
+ return () => {
36
+ document.removeEventListener('mousedown', handleMouseDown, true)
37
+ document.removeEventListener('keydown', handleKeyDown, true)
38
+ }
39
+ }, [onClose])
40
+
41
+ const handleDensityChange = (density: ExplorerDensity) => {
42
+ onChange({ density })
43
+ }
44
+
45
+ const handleScaleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
46
+ const scale = parseFloat(e.target.value)
47
+ if (!Number.isNaN(scale)) {
48
+ onChange({ scale: Math.round(scale * 100) / 100 })
49
+ }
50
+ }
51
+
52
+ const handleActionChange = (action: SingleClickAction) => {
53
+ onChange({ singleClickAction: action })
54
+ }
55
+
56
+ return (
57
+ <div ref={popoverRef} className={css.optionsPopover}>
58
+ {/* Header */}
59
+ <div className={css.optionsHeader}>
60
+ <div className={css.optionsHeaderTitle}>
61
+ <svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor" style={{ marginRight: '6px' }}>
62
+ <path d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872l-.1-.34zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z" />
63
+ </svg>
64
+ EXPLORER OPTIONS
65
+ </div>
66
+ <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
67
+ <button className={css.optionsIconBtn} title="Reset to defaults" onClick={onReset}>
68
+ <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
69
+ <path d="M13.65 2.35A7.958 7.958 0 0 0 8 0a8 8 0 1 0 8 8h-2a6 6 0 1 1-1.76-4.24l-2.24 2.24H16V0l-2.35 2.35z" />
70
+ </svg>
71
+ </button>
72
+ <button className={css.optionsIconBtn} title="Close" onClick={onClose}>
73
+ <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
74
+ <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" />
75
+ </svg>
76
+ </button>
77
+ </div>
78
+ </div>
79
+
80
+ {/* Body */}
81
+ <div className={css.optionsBody}>
82
+ {/* Section 1: Density & Scale */}
83
+ <div className={css.optionsSection}>
84
+ <div className={css.optionsSectionTitle}>Scale & Density</div>
85
+
86
+ {/* Density preset buttons */}
87
+ <div className={css.segmentGroup}>
88
+ <button
89
+ type="button"
90
+ className={`${css.segmentBtn} ${options.density === 'compact' ? css.segmentBtnActive : ''}`}
91
+ onClick={() => handleDensityChange('compact')}
92
+ >
93
+ Compact
94
+ </button>
95
+ <button
96
+ type="button"
97
+ className={`${css.segmentBtn} ${options.density === 'normal' ? css.segmentBtnActive : ''}`}
98
+ onClick={() => handleDensityChange('normal')}
99
+ >
100
+ Normal
101
+ </button>
102
+ <button
103
+ type="button"
104
+ className={`${css.segmentBtn} ${options.density === 'relaxed' ? css.segmentBtnActive : ''}`}
105
+ onClick={() => handleDensityChange('relaxed')}
106
+ >
107
+ Relaxed
108
+ </button>
109
+ </div>
110
+
111
+ {/* Scale zoom slider */}
112
+ <div className={css.optionsRow} style={{ marginTop: '6px' }}>
113
+ <div className={css.optionsLabelWrap}>
114
+ <span className={css.optionsLabel}>Zoom Scale</span>
115
+ <span className={css.optionsSubLabel}>Resize tree text and icons</span>
116
+ </div>
117
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
118
+ <span className={css.optionsBadge}>{Math.round(options.scale * 100)}%</span>
119
+ {options.scale !== 1.0 && (
120
+ <button
121
+ type="button"
122
+ className={css.optionsMiniBtn}
123
+ title="Reset scale to 100%"
124
+ onClick={() => onChange({ scale: 1.0 })}
125
+ >
126
+ 1x
127
+ </button>
128
+ )}
129
+ </div>
130
+ </div>
131
+ <input
132
+ type="range"
133
+ min="0.75"
134
+ max="1.35"
135
+ step="0.05"
136
+ value={options.scale}
137
+ onChange={handleScaleChange}
138
+ className={css.optionsSlider}
139
+ />
140
+ </div>
141
+
142
+ {/* Section 2: Motion & Animation */}
143
+ <div className={css.optionsSection}>
144
+ <div className={css.optionsSectionTitle}>Motion & Transitions</div>
145
+ <div className={css.optionsRow}>
146
+ <div className={css.optionsLabelWrap}>
147
+ <span className={css.optionsLabel}>Smooth Animations</span>
148
+ <span className={css.optionsSubLabel}>Fluid folder collapse & icon turns</span>
149
+ </div>
150
+ <div
151
+ className={`${css.switch} ${options.enableMotion ? css.switchActive : ''}`}
152
+ onClick={() => onChange({ enableMotion: !options.enableMotion })}
153
+ role="switch"
154
+ aria-checked={options.enableMotion}
155
+ >
156
+ <div className={`${css.switchKnob} ${options.enableMotion ? css.switchKnobActive : ''}`} />
157
+ </div>
158
+ </div>
159
+ </div>
160
+
161
+ {/* Section 3: File Tree Display */}
162
+ <div className={css.optionsSection}>
163
+ <div className={css.optionsSectionTitle}>Workspace View</div>
164
+
165
+ {/* Show hidden files */}
166
+ <div className={css.optionsRow}>
167
+ <div className={css.optionsLabelWrap}>
168
+ <span className={css.optionsLabel}>Show Hidden Files</span>
169
+ <span className={css.optionsSubLabel}>Include dotfiles and config directories</span>
170
+ </div>
171
+ <div
172
+ className={`${css.switch} ${options.showHiddenFiles ? css.switchActive : ''}`}
173
+ onClick={() => onChange({ showHiddenFiles: !options.showHiddenFiles })}
174
+ role="switch"
175
+ aria-checked={options.showHiddenFiles}
176
+ >
177
+ <div className={`${css.switchKnob} ${options.showHiddenFiles ? css.switchKnobActive : ''}`} />
178
+ </div>
179
+ </div>
180
+
181
+ {/* Git badges */}
182
+ <div className={css.optionsRow}>
183
+ <div className={css.optionsLabelWrap}>
184
+ <span className={css.optionsLabel}>Git Status Indicators</span>
185
+ <span className={css.optionsSubLabel}>Highlight modified & untracked files</span>
186
+ </div>
187
+ <div
188
+ className={`${css.switch} ${options.showGitBadges ? css.switchActive : ''}`}
189
+ onClick={() => onChange({ showGitBadges: !options.showGitBadges })}
190
+ role="switch"
191
+ aria-checked={options.showGitBadges}
192
+ >
193
+ <div className={`${css.switchKnob} ${options.showGitBadges ? css.switchKnobActive : ''}`} />
194
+ </div>
195
+ </div>
196
+
197
+ {/* Compact folders */}
198
+ <div className={css.optionsRow}>
199
+ <div className={css.optionsLabelWrap}>
200
+ <span className={css.optionsLabel}>Compact Folders</span>
201
+ <span className={css.optionsSubLabel}>Chain single-child directory paths</span>
202
+ </div>
203
+ <div
204
+ className={`${css.switch} ${options.compactFolders ? css.switchActive : ''}`}
205
+ onClick={() => onChange({ compactFolders: !options.compactFolders })}
206
+ role="switch"
207
+ aria-checked={options.compactFolders}
208
+ >
209
+ <div className={`${css.switchKnob} ${options.compactFolders ? css.switchKnobActive : ''}`} />
210
+ </div>
211
+ </div>
212
+ </div>
213
+
214
+ {/* Section 4: Quick Workflow */}
215
+ <div className={css.optionsSection}>
216
+ <div className={css.optionsSectionTitle}>Interaction</div>
217
+ <div className={css.optionsRow}>
218
+ <div className={css.optionsLabelWrap}>
219
+ <span className={css.optionsLabel}>File Click Behavior</span>
220
+ <span className={css.optionsSubLabel}>Direct action when clicking a file</span>
221
+ </div>
222
+ </div>
223
+ <div className={css.segmentGroup} style={{ marginTop: '4px' }}>
224
+ <button
225
+ type="button"
226
+ className={`${css.segmentBtn} ${options.singleClickAction === 'select' ? css.segmentBtnActive : ''}`}
227
+ onClick={() => handleActionChange('select')}
228
+ >
229
+ Select / Focus
230
+ </button>
231
+ <button
232
+ type="button"
233
+ className={`${css.segmentBtn} ${options.singleClickAction === 'mention' ? css.segmentBtnActive : ''}`}
234
+ onClick={() => handleActionChange('mention')}
235
+ >
236
+ Insert @Mention
237
+ </button>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ )
243
+ }