@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.
package/README.md CHANGED
@@ -27,6 +27,22 @@ DeepSeek Harness (DSH) 原生增强型文件浏览器插件。
27
27
 
28
28
  ---
29
29
 
30
+ ## 插件配置项 (设置中心 -> 插件 -> 插件配置)
31
+
32
+ 可在 DSH 设置界面的「插件配置」列表展开卡片,或直接在 `cordis.patch.yml` 中自定义配置:
33
+
34
+ - **`maxDepth`** (number,默认 `16`,范围 1~100):工作区文件树扫描的最大文件夹深度(层级)。
35
+ - **`showHidden`** (boolean,默认 `false`):默认是否显示以点开头的隐藏文件。
36
+
37
+ ```yaml
38
+ - id: dsh-workspace-files
39
+ config:
40
+ maxDepth: 20
41
+ showHidden: false
42
+ ```
43
+
44
+ ---
45
+
30
46
  ## 安装
31
47
 
32
48
  ```powershell
package/lib/client.js CHANGED
@@ -247,6 +247,24 @@ window.__ModuleLoader__.load({
247
247
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2 3h4v4H2zM10 9h4v4h-4zM4 7v6h6" })
248
248
  });
249
249
  }
250
+ function IconChevronDown({ size = 14 }) {
251
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
252
+ width: size,
253
+ height: size,
254
+ viewBox: "0 0 16 16",
255
+ fill: "none",
256
+ stroke: "currentColor",
257
+ strokeWidth: "1.5",
258
+ strokeLinecap: "round",
259
+ strokeLinejoin: "round",
260
+ style: {
261
+ display: "inline-block",
262
+ verticalAlign: "middle",
263
+ flexShrink: 0
264
+ },
265
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "4 6 8 10 12 6" })
266
+ });
267
+ }
250
268
  //#endregion
251
269
  //#region src/client/index.tsx
252
270
  const inject = ["slots", "sidebarRightTabs"];
@@ -309,6 +327,422 @@ window.__ModuleLoader__.load({
309
327
  key: OFFICIAL_FILES_TAB_ID
310
328
  }, WorkspaceFilesTabTitle)), "dsh-workspace-files: official tab title");
311
329
  }
330
+ const registerSettingsCard = (targetCtx) => {
331
+ if (!targetCtx?.slots) return;
332
+ targetCtx.effect?.(() => targetCtx.slots.inject("settings.plugin.item", function* () {
333
+ yield targetCtx.slots.register({
334
+ name: "settings.plugin.item",
335
+ key: "workspace-files"
336
+ }, WorkspaceFilesSettingsCard);
337
+ yield targetCtx.slots.register({
338
+ name: "settings.plugin.item",
339
+ key: "@civilization/dsh-workspace-files"
340
+ }, WorkspaceFilesSettingsCard);
341
+ }), "dsh-workspace-files: settings card");
342
+ };
343
+ if (slots) registerSettingsCard(ctx);
344
+ if (ctx.inject) ctx.inject(["settingsScope"], (scoped) => {
345
+ registerSettingsCard(scoped);
346
+ });
347
+ }
348
+ const CONFIG_STORAGE_KEY = "dsh_workspace_files_config";
349
+ function loadWorkspaceFilesConfig() {
350
+ try {
351
+ const raw = typeof localStorage !== "undefined" ? localStorage.getItem(CONFIG_STORAGE_KEY) : null;
352
+ if (raw) {
353
+ const parsed = JSON.parse(raw);
354
+ return {
355
+ maxDepth: typeof parsed.maxDepth === "number" && parsed.maxDepth > 0 ? parsed.maxDepth : 16,
356
+ showHidden: Boolean(parsed.showHidden)
357
+ };
358
+ }
359
+ } catch {}
360
+ return {
361
+ maxDepth: 16,
362
+ showHidden: false
363
+ };
364
+ }
365
+ function saveWorkspaceFilesConfig(cfg) {
366
+ try {
367
+ if (typeof localStorage !== "undefined") localStorage.setItem(CONFIG_STORAGE_KEY, JSON.stringify(cfg));
368
+ if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent("dsh-workspace-files:config-change", { detail: cfg }));
369
+ fetch("/dsh-workspace-files/api/config.set", {
370
+ method: "POST",
371
+ headers: { "content-type": "application/json" },
372
+ body: JSON.stringify(cfg)
373
+ }).catch(() => {});
374
+ } catch {}
375
+ }
376
+ function ensureSettingsStyle() {
377
+ if (typeof document === "undefined") return;
378
+ const id = "dsh-workspace-files-settings-style";
379
+ if (document.getElementById(id)) return;
380
+ const style = document.createElement("style");
381
+ style.id = id;
382
+ style.textContent = `
383
+ .dsh-wf-card {
384
+ border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
385
+ background: var(--dsw-alias-bg-layer-3, #fff);
386
+ border-radius: 12px;
387
+ list-style: none;
388
+ transition: border-color .16s, background .16s;
389
+ box-sizing: border-box;
390
+ }
391
+ .dsh-wf-card-open {
392
+ background: var(--dsw-alias-bg-layer-2, #f7f8fa);
393
+ border-color: var(--dsw-alias-label-dimmed, #c8ccd4);
394
+ }
395
+ .dsh-wf-header {
396
+ -webkit-appearance: none;
397
+ appearance: none;
398
+ width: 100%;
399
+ font: inherit;
400
+ color: inherit;
401
+ text-align: left;
402
+ cursor: pointer;
403
+ background: 0 0;
404
+ border: 0;
405
+ border-radius: 12px;
406
+ align-items: center;
407
+ gap: 12px;
408
+ padding: 14px 16px;
409
+ display: flex;
410
+ }
411
+ .dsh-wf-head-text {
412
+ flex-direction: column;
413
+ flex: 1;
414
+ gap: 4px;
415
+ min-width: 0;
416
+ display: flex;
417
+ }
418
+ .dsh-wf-name {
419
+ color: var(--dsw-alias-label-primary, #1f2328);
420
+ font-size: 15px;
421
+ font-weight: 600;
422
+ line-height: 1.4;
423
+ display: flex;
424
+ align-items: center;
425
+ gap: 8px;
426
+ }
427
+ .dsh-wf-version {
428
+ font-size: 12px;
429
+ font-weight: 400;
430
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
431
+ }
432
+ .dsh-wf-desc {
433
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
434
+ font-size: 13px;
435
+ line-height: 1.5;
436
+ }
437
+ .dsh-wf-chevron {
438
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
439
+ flex: none;
440
+ transition: transform .16s ease-in-out;
441
+ display: inline-flex;
442
+ }
443
+ .dsh-wf-chevron-open {
444
+ transform: rotate(180deg);
445
+ }
446
+ .dsh-wf-body {
447
+ border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
448
+ margin: 0 16px;
449
+ padding: 8px 0 12px;
450
+ }
451
+ .dsh-wf-row {
452
+ align-items: center;
453
+ gap: 16px;
454
+ padding: 12px 0;
455
+ display: flex;
456
+ justify-content: space-between;
457
+ }
458
+ .dsh-wf-row + .dsh-wf-row {
459
+ border-top: 1px solid var(--dsw-alias-border-l2, rgba(118, 137, 166, 0.12));
460
+ }
461
+ .dsh-wf-label-box {
462
+ flex-direction: column;
463
+ flex: 1;
464
+ gap: 3px;
465
+ min-width: 0;
466
+ display: flex;
467
+ }
468
+ .dsh-wf-label {
469
+ font-size: 13.5px;
470
+ font-weight: 550;
471
+ color: var(--dsw-alias-label-primary, #1f2328);
472
+ }
473
+ .dsh-wf-hint {
474
+ color: var(--dsw-alias-label-tertiary, #8b93a1);
475
+ font-size: 12px;
476
+ line-height: 18px;
477
+ }
478
+ .dsh-wf-ctrl {
479
+ flex-shrink: 0;
480
+ display: flex;
481
+ align-items: center;
482
+ gap: 8px;
483
+ }
484
+ .dsh-wf-seg {
485
+ border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
486
+ border-radius: 8px;
487
+ gap: 2px;
488
+ padding: 2px;
489
+ display: inline-flex;
490
+ background: var(--dsw-alias-bg-layer-1, rgba(255, 255, 255, 0.05));
491
+ }
492
+ .dsh-wf-seg-btn {
493
+ font: inherit;
494
+ color: var(--dsw-alias-label-secondary, #6b7280);
495
+ cursor: pointer;
496
+ background: 0 0;
497
+ border: none;
498
+ border-radius: 6px;
499
+ padding: 4px 10px;
500
+ font-size: 12px;
501
+ line-height: 18px;
502
+ transition: all 0.15s;
503
+ }
504
+ .dsh-wf-seg-btn:hover {
505
+ color: var(--dsw-alias-label-primary, #1f2328);
506
+ }
507
+ .dsh-wf-seg-active {
508
+ background: var(--dsw-alias-bg-layer-3, #eef0f4);
509
+ color: var(--dsw-alias-brand-primary, #4f6ef7);
510
+ font-weight: 600;
511
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
512
+ }
513
+ .dsh-wf-input {
514
+ box-sizing: border-box;
515
+ border: 1px solid var(--dsw-alias-border-l2, #d1d5db);
516
+ background: var(--dsw-alias-bg-layer-3, #fff);
517
+ width: 64px;
518
+ text-align: center;
519
+ color: var(--dsw-alias-label-primary, #1f2328);
520
+ font: inherit;
521
+ border-radius: 7px;
522
+ padding: 4px 6px;
523
+ font-size: 12.5px;
524
+ line-height: 18px;
525
+ }
526
+ .dsh-wf-input:focus {
527
+ border-color: var(--dsw-alias-brand-primary, #4f6ef7);
528
+ outline: 2px solid color-mix(in srgb, var(--dsw-alias-brand-primary, #4f6ef7) 18%, transparent);
529
+ }
530
+ .dsh-wf-toggle {
531
+ position: relative;
532
+ display: inline-block;
533
+ width: 40px;
534
+ height: 22px;
535
+ cursor: pointer;
536
+ }
537
+ .dsh-wf-toggle input {
538
+ opacity: 0;
539
+ width: 0;
540
+ height: 0;
541
+ position: absolute;
542
+ }
543
+ .dsh-wf-toggle-track {
544
+ position: absolute;
545
+ top: 0; left: 0; right: 0; bottom: 0;
546
+ background-color: var(--dsw-alias-border-l2, #ccc);
547
+ transition: .2s cubic-bezier(.4, 0, .2, 1);
548
+ border-radius: 22px;
549
+ }
550
+ .dsh-wf-toggle input:checked + .dsh-wf-toggle-track {
551
+ background-color: var(--dsw-alias-brand-primary, #4f6ef7);
552
+ }
553
+ .dsh-wf-toggle-thumb {
554
+ position: absolute;
555
+ height: 16px;
556
+ width: 16px;
557
+ left: 3px;
558
+ bottom: 3px;
559
+ background-color: white;
560
+ transition: .2s cubic-bezier(.4, 0, .2, 1);
561
+ border-radius: 50%;
562
+ box-shadow: 0 1px 3px rgba(0,0,0,0.2);
563
+ }
564
+ .dsh-wf-toggle input:checked + .dsh-wf-toggle-track .dsh-wf-toggle-thumb {
565
+ transform: translateX(18px);
566
+ }
567
+ .dsh-wf-footer {
568
+ margin-top: 8px;
569
+ padding-top: 10px;
570
+ border-top: 1px solid var(--dsw-alias-border-l2, rgba(118, 137, 166, 0.12));
571
+ display: flex;
572
+ align-items: center;
573
+ justify-content: space-between;
574
+ }
575
+ .dsh-wf-saved-hint {
576
+ font-size: 11.5px;
577
+ color: var(--dsw-alias-state-success-primary, #10b981);
578
+ display: flex;
579
+ align-items: center;
580
+ gap: 4px;
581
+ }
582
+ .dsh-wf-btn-reset {
583
+ background: transparent;
584
+ border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
585
+ border-radius: 6px;
586
+ padding: 3px 10px;
587
+ font-size: 12px;
588
+ color: var(--dsw-alias-label-secondary, #6b7280);
589
+ cursor: pointer;
590
+ transition: all 0.15s;
591
+ }
592
+ .dsh-wf-btn-reset:hover {
593
+ background: var(--dsw-alias-bg-layer-2, rgba(255,255,255,0.08));
594
+ color: var(--dsw-alias-label-primary, #1f2328);
595
+ }
596
+ `;
597
+ document.head.appendChild(style);
598
+ }
599
+ function WorkspaceFilesSettingsCard() {
600
+ const [open, setOpen] = (0, react.useState)(false);
601
+ const [config, setConfig] = (0, react.useState)(() => loadWorkspaceFilesConfig());
602
+ const [savedTick, setSavedTick] = (0, react.useState)(false);
603
+ (0, react.useEffect)(() => {
604
+ ensureSettingsStyle();
605
+ }, []);
606
+ const updateConfig = (patch) => {
607
+ const next = {
608
+ ...config,
609
+ ...patch
610
+ };
611
+ setConfig(next);
612
+ saveWorkspaceFilesConfig(next);
613
+ setSavedTick(true);
614
+ setTimeout(() => setSavedTick(false), 2e3);
615
+ };
616
+ const handleDepthSelect = (depth) => {
617
+ updateConfig({ maxDepth: depth });
618
+ };
619
+ const handleDepthInput = (e) => {
620
+ const val = parseInt(e.target.value, 10);
621
+ if (!isNaN(val) && val >= 1 && val <= 100) updateConfig({ maxDepth: val });
622
+ };
623
+ const handleToggleHidden = () => {
624
+ updateConfig({ showHidden: !config.showHidden });
625
+ };
626
+ const handleReset = () => {
627
+ const def = {
628
+ maxDepth: 16,
629
+ showHidden: false
630
+ };
631
+ setConfig(def);
632
+ saveWorkspaceFilesConfig(def);
633
+ setSavedTick(true);
634
+ setTimeout(() => setSavedTick(false), 2e3);
635
+ };
636
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
637
+ className: open ? "dsh-wf-card dsh-wf-card-open" : "dsh-wf-card",
638
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
639
+ type: "button",
640
+ className: "dsh-wf-header",
641
+ "aria-expanded": open,
642
+ onClick: () => setOpen(!open),
643
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
644
+ className: "dsh-wf-head-text",
645
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
646
+ className: "dsh-wf-name",
647
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "工作区文件" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
648
+ className: "dsh-wf-version",
649
+ children: "v0.3.0"
650
+ })]
651
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
652
+ className: "dsh-wf-desc",
653
+ children: "浏览与检索工作区文件树,支持递归目录扫描与一键引用。"
654
+ })]
655
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
656
+ className: open ? "dsh-wf-chevron dsh-wf-chevron-open" : "dsh-wf-chevron",
657
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDown, { size: 14 })
658
+ })]
659
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
660
+ className: "dsh-wf-body",
661
+ children: [
662
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
663
+ className: "dsh-wf-row",
664
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
665
+ className: "dsh-wf-label-box",
666
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
667
+ className: "dsh-wf-label",
668
+ children: "目录扫描最大深度"
669
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
670
+ className: "dsh-wf-hint",
671
+ children: "工作区递归扫描的最大目录层级(默认 16,范围 1~100)。若工程目录较深可适当调大。"
672
+ })]
673
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
674
+ className: "dsh-wf-ctrl",
675
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
676
+ className: "dsh-wf-seg",
677
+ children: [
678
+ 8,
679
+ 16,
680
+ 32,
681
+ 64
682
+ ].map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
683
+ type: "button",
684
+ className: config.maxDepth === d ? "dsh-wf-seg-btn dsh-wf-seg-active" : "dsh-wf-seg-btn",
685
+ onClick: () => handleDepthSelect(d),
686
+ children: d
687
+ }, d))
688
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
689
+ type: "number",
690
+ min: "1",
691
+ max: "100",
692
+ className: "dsh-wf-input",
693
+ value: config.maxDepth,
694
+ onChange: handleDepthInput,
695
+ title: "自定义深度 (1-100)"
696
+ })]
697
+ })]
698
+ }),
699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
700
+ className: "dsh-wf-row",
701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
702
+ className: "dsh-wf-label-box",
703
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
704
+ className: "dsh-wf-label",
705
+ children: "默认显示隐藏文件"
706
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
707
+ className: "dsh-wf-hint",
708
+ children: "打开文件树时是否默认展示以点开头的隐藏文件或文件夹(如 .gitignore, .env 等)。"
709
+ })]
710
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
711
+ className: "dsh-wf-ctrl",
712
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
713
+ className: "dsh-wf-toggle",
714
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
715
+ type: "checkbox",
716
+ checked: config.showHidden,
717
+ onChange: handleToggleHidden
718
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
719
+ className: "dsh-wf-toggle-track",
720
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsh-wf-toggle-thumb" })
721
+ })]
722
+ })
723
+ })]
724
+ }),
725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
726
+ className: "dsh-wf-footer",
727
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: savedTick ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
728
+ className: "dsh-wf-saved-hint",
729
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheck, { size: 12 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "已保存设置" })]
730
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
731
+ style: {
732
+ fontSize: 11,
733
+ color: "var(--dsw-alias-label-tertiary, #8b93a1)"
734
+ },
735
+ children: "设置改动即时生效并同步至工作区文件树"
736
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
737
+ type: "button",
738
+ className: "dsh-wf-btn-reset",
739
+ onClick: handleReset,
740
+ children: "恢复默认"
741
+ })]
742
+ })
743
+ ]
744
+ })]
745
+ });
312
746
  }
313
747
  function WorkspaceFilesView({ sessionId, cwd, onOpenFile }) {
314
748
  const [data, setData] = (0, react.useState)(null);
@@ -317,13 +751,24 @@ window.__ModuleLoader__.load({
317
751
  const [query, setQuery] = (0, react.useState)("");
318
752
  const [toast, setToast] = (0, react.useState)(null);
319
753
  const [expandedPaths, setExpandedPaths] = (0, react.useState)(() => /* @__PURE__ */ new Set());
320
- const [showHidden, setShowHidden] = (0, react.useState)(false);
754
+ const [activeConfig, setActiveConfig] = (0, react.useState)(() => loadWorkspaceFilesConfig());
755
+ const [showHidden, setShowHidden] = (0, react.useState)(() => loadWorkspaceFilesConfig().showHidden);
321
756
  const toastTimer = (0, react.useRef)(null);
322
757
  const showToastMsg = (0, react.useCallback)((msg) => {
323
758
  setToast(msg);
324
759
  if (toastTimer.current) window.clearTimeout(toastTimer.current);
325
760
  toastTimer.current = window.setTimeout(() => setToast(null), 2e3);
326
761
  }, []);
762
+ (0, react.useEffect)(() => {
763
+ const handleCfgChange = (e) => {
764
+ if (e.detail) {
765
+ setActiveConfig(e.detail);
766
+ setShowHidden(Boolean(e.detail.showHidden));
767
+ }
768
+ };
769
+ window.addEventListener("dsh-workspace-files:config-change", handleCfgChange);
770
+ return () => window.removeEventListener("dsh-workspace-files:config-change", handleCfgChange);
771
+ }, []);
327
772
  const loadTree = (0, react.useCallback)(async (quiet = false) => {
328
773
  if (!quiet) setLoading(true);
329
774
  setError(null);
@@ -331,7 +776,8 @@ window.__ModuleLoader__.load({
331
776
  const res = await call("files.tree", {
332
777
  sessionId,
333
778
  cwd,
334
- showHidden
779
+ showHidden,
780
+ maxDepth: activeConfig.maxDepth
335
781
  });
336
782
  setData(res);
337
783
  setExpandedPaths((prev) => {
@@ -348,7 +794,8 @@ window.__ModuleLoader__.load({
348
794
  }, [
349
795
  sessionId,
350
796
  cwd,
351
- showHidden
797
+ showHidden,
798
+ activeConfig.maxDepth
352
799
  ]);
353
800
  (0, react.useEffect)(() => {
354
801
  loadTree();
@@ -1624,8 +2071,11 @@ window.__ModuleLoader__.load({
1624
2071
  ` });
1625
2072
  }
1626
2073
  //#endregion
2074
+ exports.WorkspaceFilesSettingsCard = WorkspaceFilesSettingsCard;
1627
2075
  exports.apply = apply;
1628
2076
  exports.inject = inject;
2077
+ exports.loadWorkspaceFilesConfig = loadWorkspaceFilesConfig;
2078
+ exports.saveWorkspaceFilesConfig = saveWorkspaceFilesConfig;
1629
2079
  return module.exports;
1630
2080
  }
1631
2081
  });