@civilization/dsh-workspace-files 0.2.0 → 0.3.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.
@@ -159,3 +159,11 @@ export function IconTree({ size = 13 }: { size?: number }): JSX.Element {
159
159
  )
160
160
  }
161
161
 
162
+ export function IconChevronDown({ size = 14 }: { size?: number }): JSX.Element {
163
+ return (
164
+ <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'inline-block', verticalAlign: 'middle', flexShrink: 0 }}>
165
+ <polyline points="4 6 8 10 12 6" />
166
+ </svg>
167
+ )
168
+ }
169
+
@@ -8,6 +8,7 @@ import {
8
8
  FileGlyphIcon,
9
9
  IconAt,
10
10
  IconCheck,
11
+ IconChevronDown,
11
12
  IconClose,
12
13
  IconCollapseAll,
13
14
  IconCopy,
@@ -111,6 +112,436 @@ export function apply(ctx: Context): void {
111
112
  key: OFFICIAL_FILES_TAB_ID,
112
113
  }, WorkspaceFilesTabTitle)), 'dsh-workspace-files: official tab title')
113
114
  }
115
+
116
+ // 注入设置中心“插件配置”卡片(命名空间必须为 workspace-files,与 Host settings 服务注册一致)
117
+ const registerSettingsCard = (targetCtx: any) => {
118
+ if (!targetCtx?.slots) return
119
+ targetCtx.effect?.(() => targetCtx.slots.inject('settings.plugin.item', function* () {
120
+ yield targetCtx.slots.register({
121
+ name: 'settings.plugin.item',
122
+ key: 'workspace-files',
123
+ }, WorkspaceFilesSettingsCard)
124
+ yield targetCtx.slots.register({
125
+ name: 'settings.plugin.item',
126
+ key: '@civilization/dsh-workspace-files',
127
+ }, WorkspaceFilesSettingsCard)
128
+ }), 'dsh-workspace-files: settings card')
129
+ }
130
+
131
+ if (slots) {
132
+ registerSettingsCard(ctx)
133
+ }
134
+ if ((ctx as any).inject) {
135
+ (ctx as any).inject(['settingsScope'], (scoped: any) => {
136
+ registerSettingsCard(scoped)
137
+ })
138
+ }
139
+ }
140
+
141
+ /* ── Workspace Files Configuration Store ── */
142
+ export interface WorkspaceFilesConfig {
143
+ maxDepth: number
144
+ showHidden: boolean
145
+ }
146
+
147
+ const CONFIG_STORAGE_KEY = 'dsh_workspace_files_config'
148
+
149
+ export function loadWorkspaceFilesConfig(): WorkspaceFilesConfig {
150
+ try {
151
+ const raw = typeof localStorage !== 'undefined' ? localStorage.getItem(CONFIG_STORAGE_KEY) : null
152
+ if (raw) {
153
+ const parsed = JSON.parse(raw)
154
+ return {
155
+ maxDepth: typeof parsed.maxDepth === 'number' && parsed.maxDepth > 0 ? parsed.maxDepth : 16,
156
+ showHidden: Boolean(parsed.showHidden),
157
+ }
158
+ }
159
+ } catch {}
160
+ return { maxDepth: 16, showHidden: false }
161
+ }
162
+
163
+ export function saveWorkspaceFilesConfig(cfg: WorkspaceFilesConfig): void {
164
+ try {
165
+ if (typeof localStorage !== 'undefined') {
166
+ localStorage.setItem(CONFIG_STORAGE_KEY, JSON.stringify(cfg))
167
+ }
168
+ if (typeof window !== 'undefined') {
169
+ window.dispatchEvent(new CustomEvent('dsh-workspace-files:config-change', { detail: cfg }))
170
+ }
171
+ fetch('/dsh-workspace-files/api/config.set', {
172
+ method: 'POST',
173
+ headers: { 'content-type': 'application/json' },
174
+ body: JSON.stringify(cfg),
175
+ }).catch(() => {})
176
+ } catch {}
177
+ }
178
+
179
+ function ensureSettingsStyle() {
180
+ if (typeof document === 'undefined') return
181
+ const id = 'dsh-workspace-files-settings-style'
182
+ if (document.getElementById(id)) return
183
+ const style = document.createElement('style')
184
+ style.id = id
185
+ style.textContent = `
186
+ .dsh-wf-card {
187
+ border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
188
+ background: var(--dsw-alias-bg-layer-3, #fff);
189
+ border-radius: 12px;
190
+ list-style: none;
191
+ transition: border-color .16s, background .16s;
192
+ box-sizing: border-box;
193
+ }
194
+ .dsh-wf-card-open {
195
+ background: var(--dsw-alias-bg-layer-2, #f7f8fa);
196
+ border-color: var(--dsw-alias-label-dimmed, #c8ccd4);
197
+ }
198
+ .dsh-wf-header {
199
+ -webkit-appearance: none;
200
+ appearance: none;
201
+ width: 100%;
202
+ font: inherit;
203
+ color: inherit;
204
+ text-align: left;
205
+ cursor: pointer;
206
+ background: 0 0;
207
+ border: 0;
208
+ border-radius: 12px;
209
+ align-items: center;
210
+ gap: 12px;
211
+ padding: 14px 16px;
212
+ display: flex;
213
+ }
214
+ .dsh-wf-head-text {
215
+ flex-direction: column;
216
+ flex: 1;
217
+ gap: 4px;
218
+ min-width: 0;
219
+ display: flex;
220
+ }
221
+ .dsh-wf-name {
222
+ color: var(--dsw-alias-label-primary, #1f2328);
223
+ font-size: 15px;
224
+ font-weight: 600;
225
+ line-height: 1.4;
226
+ display: flex;
227
+ align-items: center;
228
+ gap: 8px;
229
+ }
230
+ .dsh-wf-version {
231
+ font-size: 12px;
232
+ font-weight: 400;
233
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
234
+ }
235
+ .dsh-wf-desc {
236
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
237
+ font-size: 13px;
238
+ line-height: 1.5;
239
+ }
240
+ .dsh-wf-chevron {
241
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
242
+ flex: none;
243
+ transition: transform .16s ease-in-out;
244
+ display: inline-flex;
245
+ }
246
+ .dsh-wf-chevron-open {
247
+ transform: rotate(180deg);
248
+ }
249
+ .dsh-wf-body {
250
+ border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
251
+ margin: 0 16px;
252
+ padding: 8px 0 12px;
253
+ }
254
+ .dsh-wf-row {
255
+ align-items: center;
256
+ gap: 16px;
257
+ padding: 12px 0;
258
+ display: flex;
259
+ justify-content: space-between;
260
+ }
261
+ .dsh-wf-row + .dsh-wf-row {
262
+ border-top: 1px solid var(--dsw-alias-border-l2, rgba(118, 137, 166, 0.12));
263
+ }
264
+ .dsh-wf-label-box {
265
+ flex-direction: column;
266
+ flex: 1;
267
+ gap: 3px;
268
+ min-width: 0;
269
+ display: flex;
270
+ }
271
+ .dsh-wf-label {
272
+ font-size: 13.5px;
273
+ font-weight: 550;
274
+ color: var(--dsw-alias-label-primary, #1f2328);
275
+ }
276
+ .dsh-wf-hint {
277
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
278
+ font-size: 12px;
279
+ line-height: 18px;
280
+ }
281
+ .dsh-wf-ctrl {
282
+ flex-shrink: 0;
283
+ display: flex;
284
+ align-items: center;
285
+ gap: 8px;
286
+ }
287
+ .dsh-wf-seg {
288
+ border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
289
+ border-radius: 8px;
290
+ gap: 2px;
291
+ padding: 2px;
292
+ display: inline-flex;
293
+ background: var(--dsw-alias-bg-layer-1, rgba(255, 255, 255, 0.05));
294
+ }
295
+ .dsh-wf-seg-btn {
296
+ font: inherit;
297
+ color: var(--dsw-alias-label-secondary, #6b7280);
298
+ cursor: pointer;
299
+ background: 0 0;
300
+ border: none;
301
+ border-radius: 6px;
302
+ padding: 4px 10px;
303
+ font-size: 12px;
304
+ line-height: 18px;
305
+ transition: all 0.15s;
306
+ }
307
+ .dsh-wf-seg-btn:hover {
308
+ color: var(--dsw-alias-label-primary, #1f2328);
309
+ }
310
+ .dsh-wf-seg-active {
311
+ background: var(--dsw-alias-bg-layer-3, #eef0f4);
312
+ color: var(--dsw-alias-brand-primary, #4f6ef7);
313
+ font-weight: 600;
314
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
315
+ }
316
+ .dsh-wf-input {
317
+ box-sizing: border-box;
318
+ border: 1px solid var(--dsw-alias-border-l2, #d1d5db);
319
+ background: var(--dsw-alias-bg-layer-3, #fff);
320
+ width: 64px;
321
+ text-align: center;
322
+ color: var(--dsw-alias-label-primary, #1f2328);
323
+ font: inherit;
324
+ border-radius: 7px;
325
+ padding: 4px 6px;
326
+ font-size: 12.5px;
327
+ line-height: 18px;
328
+ }
329
+ .dsh-wf-input:focus {
330
+ border-color: var(--dsw-alias-brand-primary, #4f6ef7);
331
+ outline: 2px solid color-mix(in srgb, var(--dsw-alias-brand-primary, #4f6ef7) 18%, transparent);
332
+ }
333
+ .dsh-wf-toggle {
334
+ position: relative;
335
+ display: inline-block;
336
+ width: 40px;
337
+ height: 22px;
338
+ cursor: pointer;
339
+ }
340
+ .dsh-wf-toggle input {
341
+ opacity: 0;
342
+ width: 0;
343
+ height: 0;
344
+ position: absolute;
345
+ }
346
+ .dsh-wf-toggle-track {
347
+ position: absolute;
348
+ top: 0; left: 0; right: 0; bottom: 0;
349
+ background-color: var(--dsw-alias-border-l2, #ccc);
350
+ transition: .2s cubic-bezier(.4, 0, .2, 1);
351
+ border-radius: 22px;
352
+ }
353
+ .dsh-wf-toggle input:checked + .dsh-wf-toggle-track {
354
+ background-color: var(--dsw-alias-brand-primary, #4f6ef7);
355
+ }
356
+ .dsh-wf-toggle-thumb {
357
+ position: absolute;
358
+ height: 16px;
359
+ width: 16px;
360
+ left: 3px;
361
+ bottom: 3px;
362
+ background-color: white;
363
+ transition: .2s cubic-bezier(.4, 0, .2, 1);
364
+ border-radius: 50%;
365
+ box-shadow: 0 1px 3px rgba(0,0,0,0.2);
366
+ }
367
+ .dsh-wf-toggle input:checked + .dsh-wf-toggle-track .dsh-wf-toggle-thumb {
368
+ transform: translateX(18px);
369
+ }
370
+ .dsh-wf-footer {
371
+ margin-top: 8px;
372
+ padding-top: 10px;
373
+ border-top: 1px solid var(--dsw-alias-border-l2, rgba(118, 137, 166, 0.12));
374
+ display: flex;
375
+ align-items: center;
376
+ justify-content: space-between;
377
+ }
378
+ .dsh-wf-saved-hint {
379
+ font-size: 11.5px;
380
+ color: var(--dsw-alias-state-success-primary, #10b981);
381
+ display: flex;
382
+ align-items: center;
383
+ gap: 4px;
384
+ }
385
+ .dsh-wf-btn-reset {
386
+ background: transparent;
387
+ border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
388
+ border-radius: 6px;
389
+ padding: 3px 10px;
390
+ font-size: 12px;
391
+ color: var(--dsw-alias-label-secondary, #6b7280);
392
+ cursor: pointer;
393
+ transition: all 0.15s;
394
+ }
395
+ .dsh-wf-btn-reset:hover {
396
+ background: var(--dsw-alias-bg-layer-2, rgba(255,255,255,0.08));
397
+ color: var(--dsw-alias-label-primary, #1f2328);
398
+ }
399
+ `
400
+ document.head.appendChild(style)
401
+ }
402
+
403
+ export function WorkspaceFilesSettingsCard(): JSX.Element {
404
+ const [open, setOpen] = useState(false)
405
+ const [config, setConfig] = useState<WorkspaceFilesConfig>(() => loadWorkspaceFilesConfig())
406
+ const [savedTick, setSavedTick] = useState(false)
407
+
408
+ useEffect(() => {
409
+ ensureSettingsStyle()
410
+ }, [])
411
+
412
+ const updateConfig = (patch: Partial<WorkspaceFilesConfig>) => {
413
+ const next = { ...config, ...patch }
414
+ setConfig(next)
415
+ saveWorkspaceFilesConfig(next)
416
+ setSavedTick(true)
417
+ setTimeout(() => setSavedTick(false), 2000)
418
+ }
419
+
420
+ const handleDepthSelect = (depth: number) => {
421
+ updateConfig({ maxDepth: depth })
422
+ }
423
+
424
+ const handleDepthInput = (e: React.ChangeEvent<HTMLInputElement>) => {
425
+ const val = parseInt(e.target.value, 10)
426
+ if (!isNaN(val) && val >= 1 && val <= 100) {
427
+ updateConfig({ maxDepth: val })
428
+ }
429
+ }
430
+
431
+ const handleToggleHidden = () => {
432
+ updateConfig({ showHidden: !config.showHidden })
433
+ }
434
+
435
+ const handleReset = () => {
436
+ const def = { maxDepth: 16, showHidden: false }
437
+ setConfig(def)
438
+ saveWorkspaceFilesConfig(def)
439
+ setSavedTick(true)
440
+ setTimeout(() => setSavedTick(false), 2000)
441
+ }
442
+
443
+ return (
444
+ <div className={open ? 'dsh-wf-card dsh-wf-card-open' : 'dsh-wf-card'}>
445
+ <button
446
+ type="button"
447
+ className="dsh-wf-header"
448
+ aria-expanded={open}
449
+ onClick={() => setOpen(!open)}
450
+ >
451
+ <div className="dsh-wf-head-text">
452
+ <div className="dsh-wf-name">
453
+ <span>工作区文件</span>
454
+ <span className="dsh-wf-version">v0.3.0</span>
455
+ </div>
456
+ <div className="dsh-wf-desc">
457
+ 浏览与检索工作区文件树,支持递归目录扫描与一键引用。
458
+ </div>
459
+ </div>
460
+ <span className={open ? 'dsh-wf-chevron dsh-wf-chevron-open' : 'dsh-wf-chevron'}>
461
+ <IconChevronDown size={14} />
462
+ </span>
463
+ </button>
464
+
465
+ {open && (
466
+ <div className="dsh-wf-body">
467
+ <div className="dsh-wf-row">
468
+ <div className="dsh-wf-label-box">
469
+ <div className="dsh-wf-label">目录扫描最大深度</div>
470
+ <div className="dsh-wf-hint">
471
+ 工作区递归扫描的最大目录层级(默认 16,范围 1~100)。若工程目录较深可适当调大。
472
+ </div>
473
+ </div>
474
+ <div className="dsh-wf-ctrl">
475
+ <div className="dsh-wf-seg">
476
+ {[8, 16, 32, 64].map((d) => (
477
+ <button
478
+ key={d}
479
+ type="button"
480
+ className={config.maxDepth === d ? 'dsh-wf-seg-btn dsh-wf-seg-active' : 'dsh-wf-seg-btn'}
481
+ onClick={() => handleDepthSelect(d)}
482
+ >
483
+ {d}
484
+ </button>
485
+ ))}
486
+ </div>
487
+ <input
488
+ type="number"
489
+ min="1"
490
+ max="100"
491
+ className="dsh-wf-input"
492
+ value={config.maxDepth}
493
+ onChange={handleDepthInput}
494
+ title="自定义深度 (1-100)"
495
+ />
496
+ </div>
497
+ </div>
498
+
499
+ <div className="dsh-wf-row">
500
+ <div className="dsh-wf-label-box">
501
+ <div className="dsh-wf-label">默认显示隐藏文件</div>
502
+ <div className="dsh-wf-hint">
503
+ 打开文件树时是否默认展示以点开头的隐藏文件或文件夹(如 .gitignore, .env 等)。
504
+ </div>
505
+ </div>
506
+ <div className="dsh-wf-ctrl">
507
+ <label className="dsh-wf-toggle">
508
+ <input
509
+ type="checkbox"
510
+ checked={config.showHidden}
511
+ onChange={handleToggleHidden}
512
+ />
513
+ <span className="dsh-wf-toggle-track">
514
+ <span className="dsh-wf-toggle-thumb" />
515
+ </span>
516
+ </label>
517
+ </div>
518
+ </div>
519
+
520
+ <div className="dsh-wf-footer">
521
+ <div>
522
+ {savedTick ? (
523
+ <span className="dsh-wf-saved-hint">
524
+ <IconCheck size={12} />
525
+ <span>已保存设置</span>
526
+ </span>
527
+ ) : (
528
+ <span style={{ fontSize: 11, color: 'var(--dsw-alias-label-tertiary, #8b93a1)' }}>
529
+ 设置改动即时生效并同步至工作区文件树
530
+ </span>
531
+ )}
532
+ </div>
533
+ <button
534
+ type="button"
535
+ className="dsh-wf-btn-reset"
536
+ onClick={handleReset}
537
+ >
538
+ 恢复默认
539
+ </button>
540
+ </div>
541
+ </div>
542
+ )}
543
+ </div>
544
+ )
114
545
  }
115
546
 
116
547
  /* ── Main Workspace Files View ── */
@@ -129,7 +560,8 @@ function WorkspaceFilesView({
129
560
  const [query, setQuery] = useState('')
130
561
  const [toast, setToast] = useState<string | null>(null)
131
562
  const [expandedPaths, setExpandedPaths] = useState<Set<string>>(() => new Set())
132
- const [showHidden, setShowHidden] = useState(false)
563
+ const [activeConfig, setActiveConfig] = useState<WorkspaceFilesConfig>(() => loadWorkspaceFilesConfig())
564
+ const [showHidden, setShowHidden] = useState(() => loadWorkspaceFilesConfig().showHidden)
133
565
 
134
566
  const toastTimer = useRef<number | null>(null)
135
567
 
@@ -139,11 +571,27 @@ function WorkspaceFilesView({
139
571
  toastTimer.current = window.setTimeout(() => setToast(null), 2000)
140
572
  }, [])
141
573
 
574
+ useEffect(() => {
575
+ const handleCfgChange = (e: any) => {
576
+ if (e.detail) {
577
+ setActiveConfig(e.detail)
578
+ setShowHidden(Boolean(e.detail.showHidden))
579
+ }
580
+ }
581
+ window.addEventListener('dsh-workspace-files:config-change', handleCfgChange)
582
+ return () => window.removeEventListener('dsh-workspace-files:config-change', handleCfgChange)
583
+ }, [])
584
+
142
585
  const loadTree = useCallback(async (quiet = false) => {
143
586
  if (!quiet) setLoading(true)
144
587
  setError(null)
145
588
  try {
146
- const res = await call<WorkspaceFilesResult>('files.tree', { sessionId, cwd, showHidden })
589
+ const res = await call<WorkspaceFilesResult>('files.tree', {
590
+ sessionId,
591
+ cwd,
592
+ showHidden,
593
+ maxDepth: activeConfig.maxDepth,
594
+ })
147
595
  setData(res)
148
596
 
149
597
  // 默认折叠,并从 localStorage 恢复已记录的展开状态
@@ -159,7 +607,7 @@ function WorkspaceFilesView({
159
607
  } finally {
160
608
  if (!quiet) setLoading(false)
161
609
  }
162
- }, [sessionId, cwd, showHidden])
610
+ }, [sessionId, cwd, showHidden, activeConfig.maxDepth])
163
611
 
164
612
  useEffect(() => {
165
613
  void loadTree()
package/src/files.ts CHANGED
@@ -39,7 +39,7 @@ export async function scanWorkspaceTree(
39
39
  options: { maxDepth?: number; showHidden?: boolean; showIgnored?: boolean } = {},
40
40
  ): Promise<WorkspaceFilesResult> {
41
41
  const root = resolve(cwd)
42
- const maxDepth = options.maxDepth ?? 8
42
+ const maxDepth = options.maxDepth ?? 16
43
43
  const showHidden = options.showHidden ?? false
44
44
  const showIgnored = options.showIgnored ?? false
45
45
 
package/src/index.ts CHANGED
@@ -1,9 +1,20 @@
1
1
  import type { Context } from '@deepseek-ai/cordis'
2
+ import Schema from '@deepseek-ai/schemastery'
2
3
  import { scanWorkspaceTree } from './files.js'
3
4
 
4
5
  export const name = '@civilization/dsh-workspace-files'
5
6
  export const inject = ['webServer']
6
7
 
8
+ export interface Config {
9
+ maxDepth?: number
10
+ showHidden?: boolean
11
+ }
12
+
13
+ export const Config: Schema<Config> = Schema.object({
14
+ maxDepth: Schema.natural().min(1).max(100).default(16).description('工作区文件树扫描的最大文件夹深度(层级)'),
15
+ showHidden: Schema.boolean().default(false).description('默认是否显示以点开头的隐藏文件'),
16
+ }).description('工作区文件:浏览与检索工作区文件树')
17
+
7
18
  async function readJsonBody(req: any): Promise<Record<string, any>> {
8
19
  if (req.body && typeof req.body === 'object') {
9
20
  return req.body
@@ -79,7 +90,34 @@ function resolveCwd(ctx: Context, body: Record<string, any>): string {
79
90
  return process.cwd()
80
91
  }
81
92
 
82
- export function apply(ctx: Context): void {
93
+ export const SETTINGS_NS = 'workspace-files'
94
+
95
+ export function apply(ctx: Context, config: Config = {}): void {
96
+ const liveConfig = {
97
+ maxDepth: typeof config?.maxDepth === 'number' && config.maxDepth > 0 ? config.maxDepth : 16,
98
+ showHidden: Boolean(config?.showHidden),
99
+ }
100
+
101
+ // 关键:向 Host 的 settings 服务注册 settings namespace,使设置面板的前端控制器能够枚举到此插件卡片
102
+ if ((ctx as any).inject) {
103
+ (ctx as any).inject(['settings'], (scopedCtx: any) => {
104
+ try {
105
+ const scope = scopedCtx.settings?.register(SETTINGS_NS, Config, {
106
+ base: liveConfig,
107
+ })
108
+ const applyChange = () => {
109
+ const current = scope?.get?.()
110
+ if (typeof current?.maxDepth === 'number') liveConfig.maxDepth = current.maxDepth
111
+ if (typeof current?.showHidden === 'boolean') liveConfig.showHidden = current.showHidden
112
+ }
113
+ applyChange()
114
+ scope?.watch?.(applyChange)
115
+ } catch (err) {
116
+ console.warn('[dsh-workspace-files] failed to register settings scope:', err)
117
+ }
118
+ })
119
+ }
120
+
83
121
  const webServer = (ctx as any).webServer
84
122
  if (!webServer) return
85
123
 
@@ -92,6 +130,23 @@ export function apply(ctx: Context): void {
92
130
  const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname
93
131
  const method = pathname.startsWith(prefix) ? pathname.slice(prefix.length) : undefined
94
132
 
133
+ if (method === 'config.get') {
134
+ writeJson(res, 200, { ok: true, value: liveConfig })
135
+ return
136
+ }
137
+
138
+ if (method === 'config.set') {
139
+ try {
140
+ const body = await readJsonBody(req)
141
+ if (typeof body.maxDepth === 'number' && body.maxDepth > 0) liveConfig.maxDepth = body.maxDepth
142
+ if (typeof body.showHidden === 'boolean') liveConfig.showHidden = body.showHidden
143
+ writeJson(res, 200, { ok: true, value: liveConfig })
144
+ } catch (err: any) {
145
+ writeJson(res, 500, { ok: false, error: { message: err?.message || String(err) } })
146
+ }
147
+ return
148
+ }
149
+
95
150
  if (method !== 'files.tree') {
96
151
  writeJson(res, 404, { ok: false, error: { message: `Unknown method: ${method}` } })
97
152
  return
@@ -101,9 +156,9 @@ export function apply(ctx: Context): void {
101
156
  const body = await readJsonBody(req)
102
157
  const cwd = resolveCwd(ctx, body)
103
158
  const result = await scanWorkspaceTree(cwd, {
104
- showHidden: Boolean(body.showHidden),
159
+ showHidden: typeof body.showHidden === 'boolean' ? body.showHidden : liveConfig.showHidden,
105
160
  showIgnored: Boolean(body.showIgnored),
106
- maxDepth: typeof body.maxDepth === 'number' ? body.maxDepth : 8,
161
+ maxDepth: typeof body.maxDepth === 'number' && body.maxDepth > 0 ? body.maxDepth : liveConfig.maxDepth,
107
162
  })
108
163
  writeJson(res, 200, { ok: true, value: result })
109
164
  } catch (err: any) {