dsh-locator 0.11.8 → 0.13.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README-es.md +22 -3
  3. package/README-hi.md +22 -3
  4. package/README-pt.md +22 -3
  5. package/README.en.md +22 -3
  6. package/README.md +22 -3
  7. package/bin/darwin-arm64/indexd +0 -0
  8. package/bin/darwin-x64/indexd +0 -0
  9. package/bin/linux-arm64/indexd +0 -0
  10. package/bin/linux-x64/indexd +0 -0
  11. package/bin/win32-arm64/indexd.exe +0 -0
  12. package/bin/win32-x64/indexd.exe +0 -0
  13. package/client.js +625 -2
  14. package/lib/combined.d.ts +16 -1
  15. package/lib/combined.d.ts.map +1 -1
  16. package/lib/combined.js +47 -6
  17. package/lib/combined.js.map +1 -1
  18. package/lib/config.d.ts +18 -1
  19. package/lib/config.d.ts.map +1 -1
  20. package/lib/config.js +2 -0
  21. package/lib/config.js.map +1 -1
  22. package/lib/content-search.d.ts +9 -0
  23. package/lib/content-search.d.ts.map +1 -1
  24. package/lib/content-search.js +19 -1
  25. package/lib/content-search.js.map +1 -1
  26. package/lib/content.d.ts +35 -1
  27. package/lib/content.d.ts.map +1 -1
  28. package/lib/content.js +194 -23
  29. package/lib/content.js.map +1 -1
  30. package/lib/find.d.ts +4 -1
  31. package/lib/find.d.ts.map +1 -1
  32. package/lib/find.js +141 -19
  33. package/lib/find.js.map +1 -1
  34. package/lib/http.d.ts.map +1 -1
  35. package/lib/http.js +14 -3
  36. package/lib/http.js.map +1 -1
  37. package/lib/index.d.ts.map +1 -1
  38. package/lib/index.js +5 -1
  39. package/lib/index.js.map +1 -1
  40. package/lib/ref-tools.d.ts +18 -0
  41. package/lib/ref-tools.d.ts.map +1 -0
  42. package/lib/ref-tools.js +299 -0
  43. package/lib/ref-tools.js.map +1 -0
  44. package/lib/refs.d.ts +122 -0
  45. package/lib/refs.d.ts.map +1 -0
  46. package/lib/refs.js +150 -0
  47. package/lib/refs.js.map +1 -0
  48. package/lib/reveal.d.ts +25 -0
  49. package/lib/reveal.d.ts.map +1 -1
  50. package/lib/reveal.js +59 -0
  51. package/lib/reveal.js.map +1 -1
  52. package/lib/tools.d.ts.map +1 -1
  53. package/lib/tools.js +64 -16
  54. package/lib/tools.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/combined.ts +52 -6
  57. package/src/config.ts +20 -1
  58. package/src/content-search.ts +29 -1
  59. package/src/content.ts +183 -21
  60. package/src/find.ts +135 -21
  61. package/src/http.ts +15 -2
  62. package/src/index.ts +5 -1
  63. package/src/ref-tools.ts +287 -0
  64. package/src/refs.ts +199 -0
  65. package/src/reveal.ts +59 -0
  66. package/src/tools.ts +67 -17
package/client.js CHANGED
@@ -240,6 +240,8 @@ contentWarning: 'Index warning: {list}', contentBudget: 'Content index budget re
240
240
  copyAll: 'Copy paths', copiedAll: 'Copied {n} paths',
241
241
  contentFullIndex: 'full-index', contentReasoningIndex: 'reasoning-indexed',
242
242
  panelFilter: 'Filter loaded results…', filterEmpty: 'No loaded result matches this filter', panelLoadMore: 'Load more', panelHistory: 'Recent searches', panelHistoryClear: 'Clear',
243
+ rootFilter: 'Root', allRoots: 'All roots', exportTitle: 'Export results', exportCsv: 'CSV', exportJson: 'JSON', exportDone: 'Exported', exportFailed: 'Export failed',
244
+ includeArchived: "Archived sessions", contentToolResultsIndex: "tool-results-indexed",
243
245
  },
244
246
  zh: {
245
247
  scopeContent: '内容', scopeFiles: '文件', scopeDirs: '目录',
@@ -290,6 +292,8 @@ contentWarning: '索引警告:{list}', contentBudget: '内容索引已达预
290
292
  copyAll: '复制路径', copiedAll: '已复制 {n} 条路径',
291
293
  contentFullIndex: '全量内容', contentReasoningIndex: '含思考内容',
292
294
  panelFilter: '过滤当前结果…', filterEmpty: '已加载结果中没有符合过滤条件的内容', panelLoadMore: '加载更多', panelHistory: '最近搜索', panelHistoryClear: '清空',
295
+ rootFilter: '根目录', allRoots: '全部根目录', exportTitle: '导出结果', exportCsv: 'CSV', exportJson: 'JSON', exportDone: '已导出', exportFailed: '导出失败',
296
+ includeArchived: "包含归档会话", contentToolResultsIndex: "含工具结果",
293
297
  },
294
298
  es: {
295
299
  scopeContent: 'Contenido', scopeFiles: 'Archivos', scopeDirs: 'Carpetas',
@@ -340,6 +344,8 @@ contentWarning: 'Aviso del índice: {list}', contentBudget: 'Se alcanzó el pres
340
344
  copyAll: 'Copiar rutas', copiedAll: 'Se copiaron {n} rutas',
341
345
  contentFullIndex: 'índice completo', contentReasoningIndex: 'razonamiento indexado',
342
346
  panelFilter: 'Filtrar resultados cargados…', filterEmpty: 'Ningún resultado cargado coincide con el filtro', panelLoadMore: 'Cargar más', panelHistory: 'Búsquedas recientes', panelHistoryClear: 'Borrar',
347
+ rootFilter: 'Raíz', allRoots: 'Todas las raíces', exportTitle: 'Exportar resultados', exportCsv: 'CSV', exportJson: 'JSON', exportDone: 'Exportado', exportFailed: 'Error al exportar',
348
+ includeArchived: "Sesiones archivadas", contentToolResultsIndex: "resultados de herramientas",
343
349
  },
344
350
  pt: {
345
351
  scopeContent: 'Conteúdo', scopeFiles: 'Arquivos', scopeDirs: 'Pastas',
@@ -390,6 +396,8 @@ contentWarning: 'Aviso do índice: {list}', contentBudget: 'O orçamento do índ
390
396
  copyAll: 'Copiar caminhos', copiedAll: '{n} caminhos copiados',
391
397
  contentFullIndex: 'índice completo', contentReasoningIndex: 'raciocínio indexado',
392
398
  panelFilter: 'Filtrar resultados carregados…', filterEmpty: 'Nenhum resultado carregado corresponde ao filtro', panelLoadMore: 'Carregar mais', panelHistory: 'Buscas recentes', panelHistoryClear: 'Limpar',
399
+ rootFilter: 'Raiz', allRoots: 'Todas as raízes', exportTitle: 'Exportar resultados', exportCsv: 'CSV', exportJson: 'JSON', exportDone: 'Exportado', exportFailed: 'Falha ao exportar',
400
+ includeArchived: "Sessões arquivadas", contentToolResultsIndex: "resultados de ferramentas",
393
401
  },
394
402
  hi: {
395
403
  scopeContent: 'सामग्री', scopeFiles: 'फ़ाइलें', scopeDirs: 'फ़ोल्डर',
@@ -440,6 +448,512 @@ contentWarning: 'इंडेक्स चेतावनी: {list}', contentBu
440
448
  copyAll: 'पथ कॉपी करें', copiedAll: '{n} पथ कॉपी हुए',
441
449
  contentFullIndex: 'पूर्ण इंडेक्स', contentReasoningIndex: 'reasoning इंडेक्स',
442
450
  panelFilter: 'लोड किए गए परिणाम छाँटें…', filterEmpty: 'लोड किए गए परिणामों में फ़िल्टर से मेल नहीं', panelLoadMore: 'और लोड करें', panelHistory: 'हाल की खोजें', panelHistoryClear: 'साफ़ करें',
451
+ rootFilter: 'रूट', allRoots: 'सभी रूट', exportTitle: 'परिणाम निर्यात करें', exportCsv: 'CSV', exportJson: 'JSON', exportDone: 'निर्यात हुआ', exportFailed: 'निर्यात विफल',
452
+ includeArchived: "संग्रहीत सत्र", contentToolResultsIndex: "टूल परिणाम इंडेक्स",
453
+ },
454
+ ja: {
455
+ scopeContent: "内容",
456
+ scopeFiles: "ファイル",
457
+ scopeDirs: "フォルダ",
458
+ placeholder: "名前・パス・本文・セッションを検索… 例:ext:pdf size:>10mb dm:thisweek path:src",
459
+ searchLabel: "ファイルを検索",
460
+ dialog: "ローカルファイル検索",
461
+ closeTitle: "閉じる (Esc)",
462
+ closeLabel: "閉じる",
463
+ sortLabel: "並べ替え",
464
+ sortMtime: "更新日時",
465
+ sortName: "名前",
466
+ sortSize: "サイズ",
467
+ sortPath: "パス",
468
+ asc: "昇順",
469
+ descOrder: "降順",
470
+ groupContent: "内容",
471
+ groupFiles: "ファイル",
472
+ groupDirs: "フォルダ",
473
+ relevance: "関連度順",
474
+ contentBuilding: "インデックス作成中… {files} ファイル / {messages} メッセージ",
475
+ contentProgressFiles: "インデックス作成中… {scanned}/{total} ファイル",
476
+ contentProgressSessions: "インデックス作成中… {scanned}/{total} セッション",
477
+ contentShort: "本文とセッションを検索するには 3 文字以上入力してください (短い検索は Ctrl+F)",
478
+ contentDisabled: "コンテンツインデックスは無効です (contentIndex: false)",
479
+ contentCapped: "先頭 {n} 件",
480
+ showing: "先頭 {n} 件",
481
+ deduped: "{n} 件の名前ヒットをコンテンツに統合",
482
+ loading: "検索中…",
483
+ emptyQuery: "ファイル名・パス・本文・セッションを検索するには入力してください",
484
+ emptyResults: "一致なし · 範囲を減らすか別のキーワードを試してください",
485
+ emptyScopes: "範囲を 1 つ以上選択してください (内容・ファイル・フォルダ)",
486
+ errorPrefix: "検索に失敗しました:",
487
+ retry: "再試行",
488
+ indexing: "インデックス作成中、結果が不完全な場合があります",
489
+ statusLoading: "インデックス状態を読み込み中…",
490
+ status: "インデックス {entries} 件 · {mode} · ルート {roots} 件",
491
+ live: "リアルタイム",
492
+ snapshot: "スナップショット",
493
+ usn: "USN",
494
+ copiedPath: "パスをコピーしました",
495
+ revealed: "ファイルマネージャーで表示しました",
496
+ opened: "開きました",
497
+ inserted: "@file を挿入しました",
498
+ insertFailed: "挿入に失敗しました。@file をコピーしました",
499
+ jumped: "セッションを開きました",
500
+ jumpFailed: "セッションは開きましたが、一致箇所を特定できませんでした",
501
+ footer: "↑↓ 選択 · Enter 開く/ジャンプ · Ctrl+Shift+C パスをコピー · Ctrl+O 開く · Alt+1–3 範囲 · Esc 閉じる",
502
+ results: "内容 {content} · ファイル {files} · フォルダ {dirs} · {ms} ms",
503
+ launcher: "ローカルファイル検索 (Ctrl+Shift+F)",
504
+ justNow: "たった今",
505
+ minutesAgo: "{n} 分前",
506
+ hoursAgo: "{n} 時間前",
507
+ phraseNotFound: "会話内に一致が見つかりません (まだ読み込まれていない可能性があります)",
508
+ findOpen: "会話内を検索",
509
+ findTitle: "会話内を検索",
510
+ findAria: "会話内を検索",
511
+ findPlaceholder: "会話内を検索…",
512
+ findPlaceholderSelection: "選択範囲内を検索…",
513
+ findPrev: "前の一致 (Shift+F3)",
514
+ findNext: "次の一致 (F3)",
515
+ findClose: "閉じる (Esc)",
516
+ findCase: "大文字と小文字を区別",
517
+ findWhole: "単語単位で検索",
518
+ findWrap: "折り返し",
519
+ findReverse: "逆方向に検索",
520
+ findMode: "モード",
521
+ modeNormal: "通常",
522
+ modeExtended: "拡張",
523
+ modeRegex: "正規表現",
524
+ findScope: "選択範囲内",
525
+ findScopeChip: "選択範囲 {n} 文字",
526
+ findSessionChip: "この会話",
527
+ findNoMatch: "一致なし",
528
+ findEmpty: "この会話内を検索するには入力してください",
529
+ findInvalidRegex: "正規表現が無効です:{message}",
530
+ findInvalidEscape: "拡張エスケープが無効です:{token}",
531
+ findTimeout: "正規表現がタイムアウトしました。式を簡略化してください",
532
+ findRegexUnavailable: "ここでは正規表現検索を利用できません (worker がブロックされています)",
533
+ findWrappedTop: "先頭に戻りました",
534
+ findWrappedBottom: "末尾に戻りました",
535
+ findNoWrapEnd: "最後の一致に到達しました (折り返しオフ)",
536
+ findNoWrapStart: "最初の一致に到達しました (折り返しオフ)",
537
+ findOlder: "以前のメッセージを読み込んで再試行",
538
+ findLoadingOlder: "以前のメッセージを読み込み中…",
539
+ findLoadedOnly: "読み込み済みの範囲のみ · 以前の履歴は未読み込み",
540
+ findCapped: "上限あり",
541
+ findCopied: "一致箇所をコピーしました",
542
+ findSelectionLost: "選択範囲が無効になりました。会話全体を検索します",
543
+ findNoSession: "先に会話を開いてください",
544
+ findOptions: "オプション",
545
+ findExtendedHelp: "拡張:\\n \\r \\t \\0 \\xHH \\\\",
546
+ panelTotal: "選択 {index}/{total}",
547
+ panelRangeAll: "インデックス全体",
548
+ panelRangeSession: "この会話",
549
+ panelSessionHint: "この範囲はインデックス済みのセッションメッセージのみ検索します。会話内のライブ検索は Ctrl+F を使ってください",
550
+ panelRegexPaused: "正規表現モード中はコンテンツ範囲を一時停止",
551
+ panelCountBounded: "≥ {n}",
552
+ panelCountExact: "{n}",
553
+ contentWarning: "インデックス警告:{list}",
554
+ contentBudget: "コンテンツインデックスの上限に達しました。一部のファイルはインデックスされていません",
555
+ contentTooShort: "コンテンツ検索には 3 文字以上必要です (短い検索は Ctrl+F)",
556
+ contentTimeout: "コンテンツ検索がタイムアウトしました。キーワードまたは範囲を絞ってください",
557
+ contentBadChars: "コンテンツ検索は NUL/制御文字に対応していません",
558
+ panelCaseShort: "Aa",
559
+ panelWholeShort: "単語",
560
+ panelModeLabel: "モード",
561
+ watchDegraded: "ファイル監視が低下中 · 定期更新で動作中",
562
+ copyAll: "パスをコピー",
563
+ copiedAll: "{n} 件のパスをコピーしました",
564
+ contentFullIndex: "full-index",
565
+ contentReasoningIndex: "reasoning-indexed",
566
+ panelFilter: "読み込み済みの結果を絞り込む…",
567
+ filterEmpty: "絞り込みに一致する読み込み済みの結果はありません",
568
+ panelLoadMore: "さらに読み込む",
569
+ panelHistory: "最近の検索",
570
+ panelHistoryClear: "クリア",
571
+ rootFilter: "ルート",
572
+ allRoots: "すべてのルート",
573
+ exportTitle: "結果をエクスポート",
574
+ exportCsv: "CSV",
575
+ exportJson: "JSON",
576
+ exportDone: "エクスポートしました",
577
+ exportFailed: "エクスポートに失敗しました",
578
+ includeArchived: "アーカイブ済みセッション", contentToolResultsIndex: "ツール結果インデックス",
579
+ },
580
+ fr: {
581
+ scopeContent: "Contenu",
582
+ scopeFiles: "Fichiers",
583
+ scopeDirs: "Dossiers",
584
+ placeholder: "Recherchez noms, chemins, contenus ou sessions… p. ex. ext:pdf size:>10mb dm:thisweek path:src",
585
+ searchLabel: "Rechercher des fichiers",
586
+ dialog: "Recherche de fichiers locale",
587
+ closeTitle: "Fermer (Échap)",
588
+ closeLabel: "Fermer",
589
+ sortLabel: "Trier",
590
+ sortMtime: "Modification",
591
+ sortName: "Nom",
592
+ sortSize: "Taille",
593
+ sortPath: "Chemin",
594
+ asc: "Croissant",
595
+ descOrder: "Décroissant",
596
+ groupContent: "Contenu",
597
+ groupFiles: "Fichiers",
598
+ groupDirs: "Dossiers",
599
+ relevance: "par pertinence",
600
+ contentBuilding: "indexation… {files} fichiers / {messages} messages",
601
+ contentProgressFiles: "indexation… {scanned}/{total} fichiers",
602
+ contentProgressSessions: "indexation… {scanned}/{total} sessions",
603
+ contentShort: "Saisissez au moins 3 caractères pour chercher dans les contenus et les sessions (Ctrl+F gère les requêtes plus courtes)",
604
+ contentDisabled: "L’index de contenu est désactivé (contentIndex: false)",
605
+ contentCapped: "{n} premiers",
606
+ showing: "{n} premiers",
607
+ deduped: "{n} correspondances de noms fusionnées dans le contenu",
608
+ loading: "Recherche…",
609
+ emptyQuery: "Saisissez pour rechercher des noms, chemins, contenus et sessions",
610
+ emptyResults: "Aucun résultat · essayez moins de portées ou un autre mot-clé",
611
+ emptyScopes: "Sélectionnez au moins une portée (Contenu, Fichiers, Dossiers)",
612
+ errorPrefix: "Échec de la recherche : ",
613
+ retry: "Réessayer",
614
+ indexing: "indexation en cours, résultats peut-être incomplets",
615
+ statusLoading: "Lecture de l’état de l’index…",
616
+ status: "Index : {entries} entrées · {mode} · {roots} racines",
617
+ live: "en direct",
618
+ snapshot: "instantané",
619
+ usn: "USN",
620
+ copiedPath: "Chemin copié",
621
+ revealed: "Révélé dans le gestionnaire de fichiers",
622
+ opened: "Ouvert",
623
+ inserted: "@file inséré",
624
+ insertFailed: "Échec de l’insertion ; @file copié",
625
+ jumped: "Session ouverte",
626
+ jumpFailed: "Session ouverte, mais correspondance introuvable",
627
+ footer: "↑↓ sélectionner · Entrée ouvrir/aller à · Ctrl+Shift+C copier le chemin · Ctrl+O ouvrir · Alt+1–3 portées · Échap fermer",
628
+ results: "contenu {content} · fichiers {files} · dossiers {dirs} · {ms} ms",
629
+ launcher: "Recherche de fichiers locale (Ctrl+Shift+F)",
630
+ justNow: "à l’instant",
631
+ minutesAgo: "il y a {n} min",
632
+ hoursAgo: "il y a {n} h",
633
+ phraseNotFound: "Aucune correspondance dans la conversation (pas encore chargée ?)",
634
+ findOpen: "Rechercher dans la conversation",
635
+ findTitle: "Rechercher dans la conversation",
636
+ findAria: "Rechercher dans la conversation",
637
+ findPlaceholder: "Rechercher dans la conversation…",
638
+ findPlaceholderSelection: "Rechercher dans la sélection…",
639
+ findPrev: "Correspondance précédente (Shift+F3)",
640
+ findNext: "Correspondance suivante (F3)",
641
+ findClose: "Fermer (Échap)",
642
+ findCase: "Respecter la casse",
643
+ findWhole: "Mot entier",
644
+ findWrap: "Boucler",
645
+ findReverse: "Rechercher en arrière",
646
+ findMode: "Mode",
647
+ modeNormal: "Normal",
648
+ modeExtended: "Étendu",
649
+ modeRegex: "Regex",
650
+ findScope: "Dans la sélection",
651
+ findScopeChip: "sélection {n} car.",
652
+ findSessionChip: "cette conversation",
653
+ findNoMatch: "Aucune correspondance",
654
+ findEmpty: "Saisissez pour rechercher dans la conversation",
655
+ findInvalidRegex: "Expression régulière invalide : {message}",
656
+ findInvalidEscape: "Séquence d’échappement étendue invalide : {token}",
657
+ findTimeout: "Délai d’exécution de la regex dépassé ; simplifiez l’expression",
658
+ findRegexUnavailable: "Recherche par regex indisponible ici (worker bloqué)",
659
+ findWrappedTop: "Retour en haut",
660
+ findWrappedBottom: "Retour en bas",
661
+ findNoWrapEnd: "Dernière correspondance atteinte (boucle désactivée)",
662
+ findNoWrapStart: "Première correspondance atteinte (boucle désactivée)",
663
+ findOlder: "Chargez les messages plus anciens et réessayez",
664
+ findLoadingOlder: "Chargement des messages plus anciens…",
665
+ findLoadedOnly: "fenêtre chargée uniquement · historique antérieur non chargé",
666
+ findCapped: "limité",
667
+ findCopied: "Correspondance copiée",
668
+ findSelectionLost: "Sélection expirée ; recherche dans toute la conversation",
669
+ findNoSession: "Ouvrez d’abord une conversation",
670
+ findOptions: "Options",
671
+ findExtendedHelp: "Étendu : \\n \\r \\t \\0 \\xHH \\\\",
672
+ panelTotal: "sélectionné {index}/{total}",
673
+ panelRangeAll: "Tout l’index",
674
+ panelRangeSession: "Cette conversation",
675
+ panelSessionHint: "Cette portée ne cherche que dans les messages de session indexés ; appuyez sur Ctrl+F pour une recherche en direct dans la conversation",
676
+ panelRegexPaused: "Portée contenu suspendue en mode regex",
677
+ panelCountBounded: "≥ {n}",
678
+ panelCountExact: "{n}",
679
+ contentWarning: "Avertissement d’index : {list}",
680
+ contentBudget: "Budget de l’index de contenu atteint ; certains fichiers ne sont pas indexés",
681
+ contentTooShort: "Le contenu nécessite au moins 3 caractères (utilisez Ctrl+F pour les recherches plus courtes)",
682
+ contentTimeout: "Délai de recherche de contenu dépassé ; affinez la requête ou la portée",
683
+ contentBadChars: "La recherche de contenu ne prend pas en charge les caractères NUL/de contrôle",
684
+ panelCaseShort: "Aa",
685
+ panelWholeShort: "Mot",
686
+ panelModeLabel: "Mode",
687
+ watchDegraded: "surveillance dégradée ; actualisation périodique active",
688
+ copyAll: "Copier les chemins",
689
+ copiedAll: "{n} chemins copiés",
690
+ contentFullIndex: "full-index",
691
+ contentReasoningIndex: "reasoning-indexed",
692
+ panelFilter: "Filtrer les résultats chargés…",
693
+ filterEmpty: "Aucun résultat chargé ne correspond à ce filtre",
694
+ panelLoadMore: "Charger plus",
695
+ panelHistory: "Recherches récentes",
696
+ panelHistoryClear: "Effacer",
697
+ rootFilter: "Racine",
698
+ allRoots: "Toutes les racines",
699
+ exportTitle: "Exporter les résultats",
700
+ exportCsv: "CSV",
701
+ exportJson: "JSON",
702
+ exportDone: "Exporté",
703
+ exportFailed: "Échec de l’export",
704
+ includeArchived: "Sessions archivées", contentToolResultsIndex: "résultats d’outils indexés",
705
+ },
706
+ de: {
707
+ scopeContent: "Inhalt",
708
+ scopeFiles: "Dateien",
709
+ scopeDirs: "Ordner",
710
+ placeholder: "Namen, Pfade, Texte oder Sitzungen suchen… z. B. ext:pdf size:>10mb dm:thisweek path:src",
711
+ searchLabel: "Dateien suchen",
712
+ dialog: "Lokale Dateisuche",
713
+ closeTitle: "Schließen (Esc)",
714
+ closeLabel: "Schließen",
715
+ sortLabel: "Sortieren",
716
+ sortMtime: "Geändert",
717
+ sortName: "Name",
718
+ sortSize: "Größe",
719
+ sortPath: "Pfad",
720
+ asc: "Aufsteigend",
721
+ descOrder: "Absteigend",
722
+ groupContent: "Inhalt",
723
+ groupFiles: "Dateien",
724
+ groupDirs: "Ordner",
725
+ relevance: "nach Relevanz",
726
+ contentBuilding: "Indexierung läuft… {files} Dateien / {messages} Nachrichten",
727
+ contentProgressFiles: "Indexierung läuft… {scanned}/{total} Dateien",
728
+ contentProgressSessions: "Indexierung läuft… {scanned}/{total} Sitzungen",
729
+ contentShort: "Mindestens 3 Zeichen eingeben, um Texte und Sitzungen zu durchsuchen (Ctrl+F für kürzere Suchbegriffe)",
730
+ contentDisabled: "Inhaltsindex ist deaktiviert (contentIndex: false)",
731
+ contentCapped: "erste {n}",
732
+ showing: "erste {n}",
733
+ deduped: "{n} Namensreffer in Inhalt zusammengeführt",
734
+ loading: "Suche…",
735
+ emptyQuery: "Tippen, um Dateinamen, Pfade, Texte und Sitzungen zu durchsuchen",
736
+ emptyResults: "Keine Treffer · weniger Bereiche oder anderen Suchbegriff versuchen",
737
+ emptyScopes: "Mindestens einen Bereich auswählen (Inhalt, Dateien, Ordner)",
738
+ errorPrefix: "Suche fehlgeschlagen: ",
739
+ retry: "Erneut versuchen",
740
+ indexing: "Indexierung läuft, Ergebnisse evtl. unvollständig",
741
+ statusLoading: "Indexstatus wird gelesen…",
742
+ status: "Index {entries} Einträge · {mode} · {roots} Stammverzeichnisse",
743
+ live: "live",
744
+ snapshot: "Snapshot",
745
+ usn: "USN",
746
+ copiedPath: "Pfad kopiert",
747
+ revealed: "Im Dateimanager angezeigt",
748
+ opened: "Geöffnet",
749
+ inserted: "@file eingefügt",
750
+ insertFailed: "Einfügen fehlgeschlagen; @file kopiert",
751
+ jumped: "Sitzung geöffnet",
752
+ jumpFailed: "Sitzung geöffnet, Treffer aber nicht gefunden",
753
+ footer: "↑↓ auswählen · Enter öffnen/springen · Ctrl+Shift+C Pfad kopieren · Ctrl+O öffnen · Alt+1–3 Bereiche · Esc schließen",
754
+ results: "Inhalt {content} · Dateien {files} · Ordner {dirs} · {ms} ms",
755
+ launcher: "Lokale Dateisuche (Ctrl+Shift+F)",
756
+ justNow: "gerade eben",
757
+ minutesAgo: "vor {n} Min.",
758
+ hoursAgo: "vor {n} Std.",
759
+ phraseNotFound: "Kein Treffer in der Unterhaltung (evtl. noch nicht geladen)",
760
+ findOpen: "In Unterhaltung suchen",
761
+ findTitle: "In Unterhaltung suchen",
762
+ findAria: "In Unterhaltung suchen",
763
+ findPlaceholder: "In Unterhaltung suchen…",
764
+ findPlaceholderSelection: "In Auswahl suchen…",
765
+ findPrev: "Vorheriger Treffer (Shift+F3)",
766
+ findNext: "Nächster Treffer (F3)",
767
+ findClose: "Schließen (Esc)",
768
+ findCase: "Groß-/Kleinschreibung beachten",
769
+ findWhole: "Ganzes Wort",
770
+ findWrap: "Umlaufend suchen",
771
+ findReverse: "Rückwärts suchen",
772
+ findMode: "Modus",
773
+ modeNormal: "Normal",
774
+ modeExtended: "Erweitert",
775
+ modeRegex: "Regex",
776
+ findScope: "In Auswahl",
777
+ findScopeChip: "Auswahl {n} Zeichen",
778
+ findSessionChip: "diese Unterhaltung",
779
+ findNoMatch: "Keine Treffer",
780
+ findEmpty: "Tippen, um in dieser Unterhaltung zu suchen",
781
+ findInvalidRegex: "Ungültiger regulärer Ausdruck: {message}",
782
+ findInvalidEscape: "Ungültige Extended-Escape-Sequenz: {token}",
783
+ findTimeout: "Zeitüberschreitung bei Regex; Ausdruck vereinfachen",
784
+ findRegexUnavailable: "Regex-Suche ist hier nicht verfügbar (Worker blockiert)",
785
+ findWrappedTop: "Am Anfang fortgesetzt",
786
+ findWrappedBottom: "Am Ende fortgesetzt",
787
+ findNoWrapEnd: "Letzter Treffer erreicht (Umlauf aus)",
788
+ findNoWrapStart: "Erster Treffer erreicht (Umlauf aus)",
789
+ findOlder: "Frühere Nachrichten laden und erneut versuchen",
790
+ findLoadingOlder: "Frühere Nachrichten werden geladen…",
791
+ findLoadedOnly: "nur geladener Bereich · frühere Historie nicht geladen",
792
+ findCapped: "begrenzt",
793
+ findCopied: "Treffer kopiert",
794
+ findSelectionLost: "Auswahl abgelaufen; gesamte Unterhaltung wird durchsucht",
795
+ findNoSession: "Zuerst eine Unterhaltung öffnen",
796
+ findOptions: "Optionen",
797
+ findExtendedHelp: "Erweitert: \\n \\r \\t \\0 \\xHH \\\\",
798
+ panelTotal: "ausgewählt {index}/{total}",
799
+ panelRangeAll: "Gesamter Index",
800
+ panelRangeSession: "Diese Unterhaltung",
801
+ panelSessionHint: "Dieser Bereich durchsucht nur indizierte Sitzungsnachrichten; Ctrl+F für die Live-Suche in der Unterhaltung",
802
+ panelRegexPaused: "Inhaltssuche im Regex-Modus pausiert",
803
+ panelCountBounded: "≥ {n}",
804
+ panelCountExact: "{n}",
805
+ contentWarning: "Indexwarnung: {list}",
806
+ contentBudget: "Budget des Inhaltsindex erreicht; einige Dateien sind nicht indiziert",
807
+ contentTooShort: "Inhalt braucht mindestens 3 Zeichen (Ctrl+F für kürzere Suchen)",
808
+ contentTimeout: "Zeitüberschreitung bei der Inhaltssuche; Suchanfrage oder Bereich einschränken",
809
+ contentBadChars: "Inhaltssuche unterstützt keine NUL-/Steuerzeichen",
810
+ panelCaseShort: "Aa",
811
+ panelWholeShort: "Wort",
812
+ panelModeLabel: "Modus",
813
+ watchDegraded: "Watcher eingeschränkt; regelmäßige Aktualisierung aktiv",
814
+ copyAll: "Pfade kopieren",
815
+ copiedAll: "{n} Pfade kopiert",
816
+ contentFullIndex: "full-index",
817
+ contentReasoningIndex: "reasoning-indexed",
818
+ panelFilter: "Geladene Ergebnisse filtern…",
819
+ filterEmpty: "Kein geladenes Ergebnis entspricht diesem Filter",
820
+ panelLoadMore: "Mehr laden",
821
+ panelHistory: "Letzte Suchen",
822
+ panelHistoryClear: "Löschen",
823
+ rootFilter: "Stammverzeichnis",
824
+ allRoots: "Alle Stammverzeichnisse",
825
+ exportTitle: "Ergebnisse exportieren",
826
+ exportCsv: "CSV",
827
+ exportJson: "JSON",
828
+ exportDone: "Exportiert",
829
+ exportFailed: "Export fehlgeschlagen",
830
+ includeArchived: "Archivierte Sitzungen", contentToolResultsIndex: "Tool-Ergebnisse indiziert",
831
+ },
832
+ ru: {
833
+ scopeContent: "Содержимое",
834
+ scopeFiles: "Файлы",
835
+ scopeDirs: "Папки",
836
+ placeholder: "Поиск по именам, путям, содержимому или сессиям… напр. ext:pdf size:>10mb dm:thisweek path:src",
837
+ searchLabel: "Поиск файлов",
838
+ dialog: "Локальный поиск файлов",
839
+ closeTitle: "Закрыть (Esc)",
840
+ closeLabel: "Закрыть",
841
+ sortLabel: "Сортировка",
842
+ sortMtime: "По изменению",
843
+ sortName: "По имени",
844
+ sortSize: "По размеру",
845
+ sortPath: "По пути",
846
+ asc: "По возрастанию",
847
+ descOrder: "По убыванию",
848
+ groupContent: "Содержимое",
849
+ groupFiles: "Файлы",
850
+ groupDirs: "Папки",
851
+ relevance: "по релевантности",
852
+ contentBuilding: "индексация… {files} файлов / {messages} сообщений",
853
+ contentProgressFiles: "индексация… {scanned}/{total} файлов",
854
+ contentProgressSessions: "индексация… {scanned}/{total} сессий",
855
+ contentShort: "Введите не менее 3 символов для поиска по содержимому и сессиям (для коротких запросов используйте Ctrl+F)",
856
+ contentDisabled: "Индекс содержимого отключён (contentIndex: false)",
857
+ contentCapped: "первые {n}",
858
+ showing: "первые {n}",
859
+ deduped: "{n} совпадений по именам объединено с содержимым",
860
+ loading: "Поиск…",
861
+ emptyQuery: "Введите запрос для поиска по именам файлов, путям, содержимому и сессиям",
862
+ emptyResults: "Нет совпадений · сократите области поиска или смените запрос",
863
+ emptyScopes: "Выберите хотя бы одну область (Содержимое, Файлы, Папки)",
864
+ errorPrefix: "Ошибка поиска: ",
865
+ retry: "Повторить",
866
+ indexing: "идёт индексация, результаты могут быть неполными",
867
+ statusLoading: "Чтение состояния индекса…",
868
+ status: "Индекс: {entries} записей · {mode} · корней: {roots}",
869
+ live: "в реальном времени",
870
+ snapshot: "снимок",
871
+ usn: "USN",
872
+ copiedPath: "Путь скопирован",
873
+ revealed: "Показано в файловом менеджере",
874
+ opened: "Открыто",
875
+ inserted: "Вставлено @file",
876
+ insertFailed: "Вставка не удалась; @file скопирован",
877
+ jumped: "Сессия открыта",
878
+ jumpFailed: "Сессия открыта, но совпадение не найдено",
879
+ footer: "↑↓ выбрать · Enter открыть/перейти · Ctrl+Shift+C копировать путь · Ctrl+O открыть · Alt+1–3 области · Esc закрыть",
880
+ results: "содержимое {content} · файлы {files} · папки {dirs} · {ms} мс",
881
+ launcher: "Локальный поиск файлов (Ctrl+Shift+F)",
882
+ justNow: "только что",
883
+ minutesAgo: "{n} мин назад",
884
+ hoursAgo: "{n} ч назад",
885
+ phraseNotFound: "В беседе нет совпадений (возможно, она ещё не загружена)",
886
+ findOpen: "Найти в беседе",
887
+ findTitle: "Найти в беседе",
888
+ findAria: "Найти в беседе",
889
+ findPlaceholder: "Найти в беседе…",
890
+ findPlaceholderSelection: "Найти в выделении…",
891
+ findPrev: "Предыдущее совпадение (Shift+F3)",
892
+ findNext: "Следующее совпадение (F3)",
893
+ findClose: "Закрыть (Esc)",
894
+ findCase: "С учётом регистра",
895
+ findWhole: "Слово целиком",
896
+ findWrap: "Переход по кругу",
897
+ findReverse: "Обратный поиск",
898
+ findMode: "Режим",
899
+ modeNormal: "Обычный",
900
+ modeExtended: "Расширенный",
901
+ modeRegex: "Regex",
902
+ findScope: "В выделении",
903
+ findScopeChip: "выделено {n} симв.",
904
+ findSessionChip: "эта беседа",
905
+ findNoMatch: "Нет совпадений",
906
+ findEmpty: "Введите текст для поиска в этой беседе",
907
+ findInvalidRegex: "Некорректное регулярное выражение: {message}",
908
+ findInvalidEscape: "Некорректный escape в расширенном режиме: {token}",
909
+ findTimeout: "Время ожидания regex истекло; упростите выражение",
910
+ findRegexUnavailable: "Поиск по regex здесь недоступен (worker заблокирован)",
911
+ findWrappedTop: "Переход к началу",
912
+ findWrappedBottom: "Переход к концу",
913
+ findNoWrapEnd: "Достигнуто последнее совпадение (переход по кругу выключен)",
914
+ findNoWrapStart: "Достигнуто первое совпадение (переход по кругу выключен)",
915
+ findOlder: "Загрузите более ранние сообщения и повторите",
916
+ findLoadingOlder: "Загрузка более ранних сообщений…",
917
+ findLoadedOnly: "только загруженное окно · ранняя история не загружена",
918
+ findCapped: "ограничено",
919
+ findCopied: "Совпадение скопировано",
920
+ findSelectionLost: "Выделение сброшено; поиск по всей беседе",
921
+ findNoSession: "Сначала откройте беседу",
922
+ findOptions: "Параметры",
923
+ findExtendedHelp: "Расширенный: \\n \\r \\t \\0 \\xHH \\\\",
924
+ panelTotal: "выбрано {index}/{total}",
925
+ panelRangeAll: "Весь индекс",
926
+ panelRangeSession: "Эта беседа",
927
+ panelSessionHint: "Эта область ищет только проиндексированные сообщения сессий; для поиска в текущей беседе нажмите Ctrl+F",
928
+ panelRegexPaused: "В режиме regex поиск по содержимому приостановлен",
929
+ panelCountBounded: "≥ {n}",
930
+ panelCountExact: "{n}",
931
+ contentWarning: "Предупреждение индекса: {list}",
932
+ contentBudget: "Достигнут лимит индекса содержимого; часть файлов не проиндексирована",
933
+ contentTooShort: "Для поиска по содержимому нужно не менее 3 символов (для коротких запросов используйте Ctrl+F)",
934
+ contentTimeout: "Время ожидания поиска по содержимому истекло; сузьте запрос или область",
935
+ contentBadChars: "Поиск по содержимому не поддерживает NUL и управляющие символы",
936
+ panelCaseShort: "Aa",
937
+ panelWholeShort: "Слово",
938
+ panelModeLabel: "Режим",
939
+ watchDegraded: "watcher работает в ограниченном режиме; включено периодическое обновление",
940
+ copyAll: "Копировать пути",
941
+ copiedAll: "Скопировано путей: {n}",
942
+ contentFullIndex: "full-index",
943
+ contentReasoningIndex: "reasoning-indexed",
944
+ panelFilter: "Фильтр загруженных результатов…",
945
+ filterEmpty: "Ни один загруженный результат не соответствует фильтру",
946
+ panelLoadMore: "Загрузить ещё",
947
+ panelHistory: "Недавние запросы",
948
+ panelHistoryClear: "Очистить",
949
+ rootFilter: "Корень",
950
+ allRoots: "Все корни",
951
+ exportTitle: "Экспорт результатов",
952
+ exportCsv: "CSV",
953
+ exportJson: "JSON",
954
+ exportDone: "Экспортировано",
955
+ exportFailed: "Не удалось экспортировать",
956
+ includeArchived: "Архивные сессии", contentToolResultsIndex: "результаты инструментов",
443
957
  },
444
958
  }
445
959
  const LANG = (() => {
@@ -451,6 +965,10 @@ contentWarning: 'इंडेक्स चेतावनी: {list}', contentBu
451
965
  if (tag.startsWith('es')) return 'es'
452
966
  if (tag.startsWith('pt')) return 'pt'
453
967
  if (tag.startsWith('hi')) return 'hi'
968
+ if (tag.startsWith('ja')) return 'ja'
969
+ if (tag.startsWith('fr')) return 'fr'
970
+ if (tag.startsWith('de')) return 'de'
971
+ if (tag.startsWith('ru')) return 'ru'
454
972
  return 'en'
455
973
  })()
456
974
  const TEXT = STRINGS[LANG]
@@ -1376,8 +1894,24 @@ self.onmessage = event => {
1376
1894
  const loadOlderTimerRef = useRef(0)
1377
1895
  const returnFocusRef = useRef(null)
1378
1896
  const [anchor, setAnchor] = useState(null)
1897
+ const [queryHistory, setQueryHistory] = useState(readFindHistory)
1379
1898
  const composingRef = useRef(false)
1380
1899
 
1900
+ useEffect(() => {
1901
+ if (!state.open) return undefined
1902
+ const text = state.query.trim()
1903
+ if (text.length < 2) return undefined
1904
+ const timer = setTimeout(() => {
1905
+ setQueryHistory(previous => {
1906
+ if (previous[0] === text) return previous
1907
+ const next = [text, ...previous.filter(value => value !== text)].slice(0, 20)
1908
+ writeFindHistory(next)
1909
+ return next
1910
+ })
1911
+ }, 1200)
1912
+ return () => clearTimeout(timer)
1913
+ }, [state.open, state.query])
1914
+
1381
1915
  const applyHighlight = useCallback((index) => {
1382
1916
  const matches = matchesRef.current
1383
1917
  if (matches.length === 0) { clearFindHighlight('find'); setFindScope(null); return }
@@ -1699,6 +2233,7 @@ self.onmessage = event => {
1699
2233
  placeholder: state.scope === 'selection' ? t('findPlaceholderSelection') : t('findPlaceholder'),
1700
2234
  'aria-label': t('findAria'),
1701
2235
  'aria-invalid': state.error !== '' ? 'true' : undefined,
2236
+ list: 'dsh-locator-find-history',
1702
2237
  spellCheck: false,
1703
2238
  autoComplete: 'off',
1704
2239
  onInput,
@@ -1757,6 +2292,9 @@ self.onmessage = event => {
1757
2292
  h('kbd', null, 'Shift+Enter'), h('span', null, '↑'),
1758
2293
  h('kbd', null, 'Esc'),
1759
2294
  ),
2295
+ queryHistory.length > 0
2296
+ ? h('datalist', { id: 'dsh-locator-find-history' }, queryHistory.map(value => h('option', { key: value, value })))
2297
+ : null,
1760
2298
  )
1761
2299
  }
1762
2300
 
@@ -1771,6 +2309,17 @@ self.onmessage = event => {
1771
2309
  try { localStorage.setItem(PANEL_HISTORY_KEY, JSON.stringify(list.slice(0, 10))) } catch { /* ignore */ }
1772
2310
  }
1773
2311
 
2312
+ const FIND_HISTORY_KEY = 'dsh-locator-find-history-v1'
2313
+ function readFindHistory() {
2314
+ try {
2315
+ const parsed = JSON.parse(localStorage.getItem(FIND_HISTORY_KEY) || '[]')
2316
+ return Array.isArray(parsed) ? parsed.filter(value => typeof value === 'string').slice(0, 20) : []
2317
+ } catch { return [] }
2318
+ }
2319
+ function writeFindHistory(list) {
2320
+ try { localStorage.setItem(FIND_HISTORY_KEY, JSON.stringify(list.slice(0, 20))) } catch { /* ignore */ }
2321
+ }
2322
+
1774
2323
  function Panel() {
1775
2324
  const open = useOpen()
1776
2325
  const prefs = useMemo(readPrefs, [])
@@ -1792,6 +2341,8 @@ self.onmessage = event => {
1792
2341
  const [caseSensitive, setCaseSensitive] = useState(prefs.caseSensitive === true)
1793
2342
  const [wholeWord, setWholeWord] = useState(prefs.wholeWord === true)
1794
2343
  const [sessionOnly, setSessionOnly] = useState(false)
2344
+ const [rootFilter, setRootFilter] = useState('')
2345
+ const [includeArchived, setIncludeArchived] = useState(false)
1795
2346
  const [selected, setSelected] = useState(0)
1796
2347
  const [toast, setToast] = useState('')
1797
2348
  const [refreshTick, setRefreshTick] = useState(0)
@@ -1916,6 +2467,8 @@ self.onmessage = event => {
1916
2467
  caseSensitive,
1917
2468
  wholeWord,
1918
2469
  sessionId: sessionOnly ? activeSessionId : '',
2470
+ root: rootFilter,
2471
+ includeArchived,
1919
2472
  count: matchMode === 'regex' ? 'none' : 'exact',
1920
2473
  }, controller.signal).then(data => {
1921
2474
  if (controller.signal.aborted) return
@@ -1999,6 +2552,51 @@ self.onmessage = event => {
1999
2552
  copyText(paths.join('\n')).then(() => notify(t('copiedAll', { n: paths.length }))).catch(() => {})
2000
2553
  }, [notify, rows])
2001
2554
 
2555
+ const exportRows = useCallback(format => {
2556
+ if (rows.length === 0) return
2557
+ const records = rows.map(({ group, hit }) => ({
2558
+ group,
2559
+ kind: typeof hit.kind === 'string' ? hit.kind : group,
2560
+ path: typeof hit.path === 'string' ? hit.path : '',
2561
+ name: typeof hit.name === 'string' ? hit.name : (typeof hit.title === 'string' ? hit.title : ''),
2562
+ size: typeof hit.size === 'number' ? hit.size : null,
2563
+ mtime: typeof hit.mtime === 'number' ? hit.mtime : null,
2564
+ line: typeof hit.line === 'number' ? hit.line : null,
2565
+ sessionId: typeof hit.sessionId === 'string' ? hit.sessionId : null,
2566
+ messageId: typeof hit.messageId === 'string' ? hit.messageId : null,
2567
+ snippet: typeof hit.snippet === 'string' ? hit.snippet : '',
2568
+ }))
2569
+ let text, mime, extension
2570
+ if (format === 'json') {
2571
+ text = JSON.stringify(records, null, 2)
2572
+ mime = 'application/json'
2573
+ extension = 'json'
2574
+ } else {
2575
+ const columns = ['group', 'kind', 'path', 'name', 'size', 'mtime', 'line', 'sessionId', 'messageId', 'snippet']
2576
+ const cell = value => {
2577
+ const string = value === null || value === undefined ? '' : String(value)
2578
+ return /[",\r\n]/.test(string) ? '"' + string.replace(/"/g, '""') + '"' : string
2579
+ }
2580
+ text = [columns.join(','), ...records.map(record => columns.map(column => cell(record[column])).join(','))].join('\r\n')
2581
+ mime = 'text/csv'
2582
+ extension = 'csv'
2583
+ }
2584
+ try {
2585
+ const blob = new Blob([text], { type: mime + ';charset=utf-8' })
2586
+ const url = URL.createObjectURL(blob)
2587
+ const link = document.createElement('a')
2588
+ link.href = url
2589
+ link.download = 'dsh-locator-results-' + Date.now() + '.' + extension
2590
+ document.body.appendChild(link)
2591
+ link.click()
2592
+ link.remove()
2593
+ setTimeout(() => URL.revokeObjectURL(url), 1000)
2594
+ notify(t('exportDone'))
2595
+ } catch {
2596
+ notify(t('exportFailed'))
2597
+ }
2598
+ }, [notify, rows])
2599
+
2002
2600
  const reveal = useCallback(hit => {
2003
2601
  if (!hit || loading) return
2004
2602
  post('/dsh-locator/reveal', { path: hit.path }).then(() => notify(t('revealed'))).catch(() => copy(hit))
@@ -2006,7 +2604,8 @@ self.onmessage = event => {
2006
2604
 
2007
2605
  const openFile = useCallback(hit => {
2008
2606
  if (!hit || hit.dir || loading) return
2009
- post('/dsh-locator/open', { path: hit.path }).then(() => notify(t('opened'))).catch(() => copy(hit))
2607
+ const line = typeof hit.line === 'number' ? hit.line : undefined
2608
+ post('/dsh-locator/open', { path: hit.path, line }).then(() => notify(t('opened'))).catch(() => copy(hit))
2010
2609
  }, [copy, loading, notify])
2011
2610
 
2012
2611
  const activate = useCallback(hit => {
@@ -2141,6 +2740,8 @@ self.onmessage = event => {
2141
2740
  caseSensitive,
2142
2741
  wholeWord,
2143
2742
  sessionId: sessionOnly ? activeSessionId : '',
2743
+ root: rootFilter,
2744
+ includeArchived,
2144
2745
  count: matchMode === 'regex' ? 'none' : 'exact',
2145
2746
  })
2146
2747
  setGroups(previous => {
@@ -2379,7 +2980,22 @@ self.onmessage = event => {
2379
2980
  scopeBox('content', t('scopeContent'), contentCountText),
2380
2981
  scopeBox('files', t('scopeFiles'), filesCountText),
2381
2982
  scopeBox('dirs', t('scopeDirs'), dirsCountText),
2983
+ status !== null && Array.isArray(status.roots) && status.roots.length > 1
2984
+ ? h('label', { className: 'dsh-locator-check', title: t('rootFilter') }, t('rootFilter'),
2985
+ h('select', {
2986
+ className: 'dsh-locator-sort',
2987
+ value: rootFilter,
2988
+ 'aria-label': t('rootFilter'),
2989
+ onChange: event => { setRootFilter(event.target.value); setSelected(0) },
2990
+ },
2991
+ h('option', { value: '' }, t('allRoots')),
2992
+ status.roots.map(root => h('option', { key: root, value: root, title: root }, String(root).split(/[\\/]/).filter(Boolean).pop() || root)),
2993
+ ))
2994
+ : null,
2382
2995
  h('span', { className: 'grow' }),
2996
+ h('label', { className: 'dsh-locator-check', title: t('includeArchived') },
2997
+ h('input', { type: 'checkbox', checked: includeArchived, onChange: () => setIncludeArchived(value => !value) }),
2998
+ t('includeArchived')),
2383
2999
  h('label', { className: 'dsh-locator-check' + (activeSessionId === '' ? ' disabled' : ''),
2384
3000
  title: activeSessionId === '' ? t('findNoSession') : t('panelSessionHint') },
2385
3001
  h('input', { type: 'checkbox', checked: sessionOnly, disabled: activeSessionId === '',
@@ -2425,7 +3041,8 @@ self.onmessage = event => {
2425
3041
  ? ' · ' + t('watchDegraded')
2426
3042
  : '')
2427
3043
  + (status.content && status.content.fullIndex === true ? ' · ' + t('contentFullIndex') : '')
2428
- + (status.content && status.content.reasoningIndex === true ? ' · ' + t('contentReasoningIndex') : '')),
3044
+ + (status.content && status.content.reasoningIndex === true ? ' · ' + t('contentReasoningIndex') : '')
3045
+ + (status.content && status.content.toolResultsIndex === true ? ' · ' + t('contentToolResultsIndex') : '')),
2429
3046
  ),
2430
3047
  history.length > 0
2431
3048
  ? h('div', { className: 'dsh-locator-history' },
@@ -2478,6 +3095,12 @@ self.onmessage = event => {
2478
3095
  rows.length > 0
2479
3096
  ? h('button', { className: 'dsh-locator-dirbtn', onClick: copyAll, title: t('copyAll') }, t('copyAll'))
2480
3097
  : null,
3098
+ rows.length > 0
3099
+ ? h('button', { className: 'dsh-locator-dirbtn', onClick: () => exportRows('csv'), title: t('exportTitle') }, t('exportCsv'))
3100
+ : null,
3101
+ rows.length > 0
3102
+ ? h('button', { className: 'dsh-locator-dirbtn', onClick: () => exportRows('json'), title: t('exportTitle') }, t('exportJson'))
3103
+ : null,
2481
3104
  canLoadMore
2482
3105
  ? h('button', { className: 'dsh-locator-dirbtn', disabled: loadingMore, onClick: loadMore }, loadingMore ? t('loading') : t('panelLoadMore'))
2483
3106
  : null,