codexmate 0.1.24 → 0.1.27

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 (50) hide show
  1. package/cli/session-usage.js +10 -4
  2. package/cli.js +737 -200
  3. package/lib/cli-sessions.js +1 -1
  4. package/package.json +3 -3
  5. package/plugins/prompt-templates/computed.mjs +1 -1
  6. package/plugins/prompt-templates/index.mjs +3 -3
  7. package/plugins/prompt-templates/manifest.mjs +5 -3
  8. package/plugins/prompt-templates/methods.mjs +9 -2
  9. package/plugins/registry.mjs +5 -2
  10. package/plugins/text-tools/computed.mjs +23 -0
  11. package/plugins/text-tools/index.mjs +14 -0
  12. package/plugins/text-tools/manifest.mjs +20 -0
  13. package/plugins/text-tools/methods.mjs +24 -0
  14. package/plugins/text-tools/overview.mjs +12 -0
  15. package/plugins/text-tools/ownership.mjs +7 -0
  16. package/tools/dev/launch.js +147 -0
  17. package/web-ui/app.js +15 -5
  18. package/web-ui/index.html +1 -2
  19. package/web-ui/logic.markdown-editor.mjs +5 -57
  20. package/web-ui/logic.prompts-token-count.mjs +49 -0
  21. package/web-ui/logic.sessions.mjs +30 -15
  22. package/web-ui/modules/app.computed.dashboard.mjs +1 -0
  23. package/web-ui/modules/app.computed.prompts.mjs +18 -0
  24. package/web-ui/modules/app.computed.session.mjs +3 -1
  25. package/web-ui/modules/app.methods.navigation.mjs +79 -3
  26. package/web-ui/modules/app.methods.prompts-editor.mjs +6 -64
  27. package/web-ui/modules/app.methods.session-actions.mjs +10 -4
  28. package/web-ui/modules/app.methods.session-browser.mjs +34 -45
  29. package/web-ui/modules/app.methods.session-timeline.mjs +1 -0
  30. package/web-ui/modules/app.methods.web-ui-preferences.mjs +7 -1
  31. package/web-ui/modules/i18n/locales/en.mjs +20 -1
  32. package/web-ui/modules/i18n/locales/ja.mjs +20 -1
  33. package/web-ui/modules/i18n/locales/vi.mjs +20 -1
  34. package/web-ui/modules/i18n/locales/zh-tw.mjs +20 -1
  35. package/web-ui/modules/i18n/locales/zh.mjs +20 -1
  36. package/web-ui/modules/plugins.computed.mjs +8 -2
  37. package/web-ui/modules/plugins.methods.mjs +8 -2
  38. package/web-ui/partials/index/layout-header.html +23 -6
  39. package/web-ui/partials/index/panel-plugins.html +45 -5
  40. package/web-ui/partials/index/panel-prompts.html +16 -17
  41. package/web-ui/partials/index/panel-sessions.html +19 -4
  42. package/web-ui/res/web-ui-render.precompiled.js +193 -93
  43. package/web-ui/session-helpers.mjs +1 -2
  44. package/web-ui/source-bundle.cjs +1 -0
  45. package/web-ui/styles/base-theme.css +24 -0
  46. package/web-ui/styles/modals-core.css +48 -0
  47. package/web-ui/styles/prompts-editor.css +3 -66
  48. package/web-ui/styles/sessions-list.css +1 -1
  49. package/web-ui/styles/sessions-preview.css +56 -7
  50. package/web-ui/styles/sessions-toolbar-trash.css +1 -7
@@ -114,7 +114,10 @@ function normalizeNavigationSnapshot(vm, source = {}) {
114
114
  : currentSkillsTargetApp,
115
115
  promptTemplatesMode: source.promptTemplatesMode === 'manage' || source.promptTemplatesMode === 'compose'
116
116
  ? source.promptTemplatesMode
117
- : currentPromptTemplatesMode
117
+ : currentPromptTemplatesMode,
118
+ pluginsActiveId: typeof source.pluginsActiveId === 'string'
119
+ ? source.pluginsActiveId
120
+ : (typeof vm.pluginsActiveId === 'string' ? vm.pluginsActiveId : '')
118
121
  };
119
122
  }
120
123
 
@@ -475,6 +478,9 @@ export function createWebUiPreferencesMethods(options = {}) {
475
478
  this.settingsTab = this.normalizeSettingsTab(nav.settingsTab);
476
479
  }
477
480
  if (typeof nav.configMode === 'string') this.configMode = nav.configMode;
481
+ if (typeof nav.pluginsActiveId === 'string' && nav.pluginsActiveId.trim()) {
482
+ this.pluginsActiveId = nav.pluginsActiveId.trim();
483
+ }
478
484
  if (typeof nav.mainTab === 'string') {
479
485
  if (typeof this.switchMainTab === 'function') {
480
486
  this.switchMainTab(nav.mainTab);
@@ -254,6 +254,8 @@ const en = Object.freeze({
254
254
  'prompts.editor.previewEmpty': 'Nothing to preview yet',
255
255
  'prompts.editor.changeAxis': 'Change navigation axis',
256
256
  'prompts.editor.changeAxisTick': 'Line {line} (+{added} / -{removed})',
257
+ 'prompts.tokenCount.hint': '~{count} tokens',
258
+ 'prompts.tokenCount.diff': 'Tokens: {before} before → {current} now',
257
259
  'prompts.presets.title': 'Presets',
258
260
  'prompts.presets.hint': 'Manage presets.',
259
261
  'prompts.presets.namePlaceholder': 'Preset name',
@@ -350,6 +352,8 @@ const en = Object.freeze({
350
352
  'dashboard.sessionSource.claude': 'Claude Code',
351
353
  'dashboard.sessionSource.gemini': 'Gemini CLI',
352
354
  'dashboard.sessionSource.codebuddy': 'CodeBuddy Code',
355
+
356
+ 'dashboard.sessionSource.workbuddy': 'WorkBuddy AI',
353
357
  'dashboard.sessionSource.all': 'All',
354
358
  'dashboard.sessionPath.all': 'All paths',
355
359
  'dashboard.sessionQuery.unsupported': 'Unsupported source',
@@ -384,6 +388,17 @@ const en = Object.freeze({
384
388
  'plugins.catalog.promptTemplates.title': 'Prompt templates',
385
389
  'plugins.catalog.promptTemplates.description': 'Standardized, template-driven prompt tools with variables, copy, and export helpers.',
386
390
  'plugins.status.standard': 'standard',
391
+ 'plugins.catalog.textTools.title': 'Text tools',
392
+ 'plugins.catalog.textTools.description': 'A set of lightweight plain-text utilities; the first tool compresses multi-line text into a single line.',
393
+ 'plugins.textTools.title': 'Text tools',
394
+ 'plugins.textTools.tools.aria': 'Text tool selection',
395
+ 'plugins.textTools.tools.compress': 'Compress text',
396
+ 'plugins.textTools.compress.inputLabel': 'Input text',
397
+ 'plugins.textTools.compress.inputPlaceholder': 'Paste or type the text to compress…',
398
+ 'plugins.textTools.compress.inputAria': 'Text to compress',
399
+ 'plugins.textTools.compress.outputTitle': 'Compressed result',
400
+ 'plugins.textTools.compress.outputAria': 'Compressed text result',
401
+ 'plugins.textTools.compress.copy': 'Copy',
387
402
  'plugins.main.ariaWorkspace': 'Plugin workspace',
388
403
  'plugins.refresh': 'Refresh',
389
404
  'plugins.refreshing': 'Refreshing...',
@@ -671,7 +686,10 @@ const en = Object.freeze({
671
686
  'sessions.source.claudeCode': 'Claude Code',
672
687
  'sessions.source.gemini': 'Gemini CLI',
673
688
  'sessions.source.codebuddy': 'CodeBuddy Code',
689
+
690
+ 'sessions.source.workbuddy': 'WorkBuddy AI',
674
691
  'sessions.source.pi': 'Pi',
692
+ 'sessions.source.opencode': 'OpenCode',
675
693
  'sessions.loadingList': 'Loading sessions...',
676
694
  'sessions.empty': 'No sessions found',
677
695
  'sessions.unknownTime': 'unknown time',
@@ -683,6 +701,8 @@ const en = Object.freeze({
683
701
  'sessions.unpin': 'Unpin',
684
702
  'sessions.copyResume': 'Copy resume command',
685
703
  'sessions.preview.refresh': 'Refresh content',
704
+ 'sessions.preview.scrollTop': 'Back to top',
705
+ 'sessions.preview.scrollBottom': 'Jump to bottom',
686
706
  'sessions.preview.loading': 'Loading...',
687
707
  'sessions.batch.select': 'Batch delete',
688
708
  'sessions.batch.exit': 'Exit selection',
@@ -773,7 +793,6 @@ const en = Object.freeze({
773
793
  'sessions.sort.hot': 'Sort: hot',
774
794
  'sessions.sort.hotBadge': 'Hot',
775
795
  'sessions.filters.copyLink': 'Copy filter link',
776
- 'sessions.filters.urlBuildFail': 'Failed to build link',
777
796
  'sessions.filters.source': 'Source',
778
797
  'sessions.filters.path': 'Path',
779
798
  'sessions.filters.keyword': 'Keyword',
@@ -255,6 +255,8 @@ const ja = Object.freeze({
255
255
  'prompts.editor.previewEmpty': 'プレビューする内容がありません',
256
256
  'prompts.editor.changeAxis': '変更ナビゲーション軸',
257
257
  'prompts.editor.changeAxisTick': '{line} 行目(+{added} / -{removed})',
258
+ 'prompts.tokenCount.hint': '約 {count} トークン',
259
+ 'prompts.tokenCount.diff': 'トークン:変更前 {before} → 現在 {current}',
258
260
  'prompts.presets.title': 'プリセット',
259
261
  'prompts.presets.hint': 'プリセットを管理します。',
260
262
  'prompts.presets.namePlaceholder': 'プリセット名',
@@ -352,6 +354,8 @@ const ja = Object.freeze({
352
354
  'dashboard.sessionSource.claude': 'Claude Code',
353
355
  'dashboard.sessionSource.gemini': 'Gemini CLI',
354
356
  'dashboard.sessionSource.codebuddy': 'CodeBuddy Code',
357
+
358
+ 'dashboard.sessionSource.workbuddy': 'WorkBuddy AI',
355
359
  'dashboard.sessionSource.all': 'すべて',
356
360
  'dashboard.sessionPath.all': 'すべてのパス',
357
361
  'dashboard.sessionQuery.unsupported': '現在のソースは非対応',
@@ -386,6 +390,17 @@ const ja = Object.freeze({
386
390
  'plugins.catalog.promptTemplates.title': 'プロンプトテンプレート',
387
391
  'plugins.catalog.promptTemplates.description': '変数、コピー、エクスポートに対応した標準化テンプレート型プロンプトツール。',
388
392
  'plugins.status.standard': '標準',
393
+ 'plugins.catalog.textTools.title': 'テキストツール',
394
+ 'plugins.catalog.textTools.description': '軽量なテキストユーティリティ集。最初のツールは複数行のテキストを 1 行に圧縮します。',
395
+ 'plugins.textTools.title': 'テキストツール',
396
+ 'plugins.textTools.tools.aria': 'テキストツールの選択',
397
+ 'plugins.textTools.tools.compress': 'テキスト圧縮',
398
+ 'plugins.textTools.compress.inputLabel': '入力テキスト',
399
+ 'plugins.textTools.compress.inputPlaceholder': '圧縮するテキストを貼り付けまたは入力…',
400
+ 'plugins.textTools.compress.inputAria': '圧縮するテキスト',
401
+ 'plugins.textTools.compress.outputTitle': '圧縮結果',
402
+ 'plugins.textTools.compress.outputAria': '圧縮後のテキスト結果',
403
+ 'plugins.textTools.compress.copy': 'コピー',
389
404
  'plugins.main.ariaWorkspace': 'プラグインワークスペース',
390
405
  'plugins.refresh': '更新',
391
406
  'plugins.refreshing': '更新中...',
@@ -673,7 +688,10 @@ const ja = Object.freeze({
673
688
  'sessions.source.claudeCode': 'Claude Code',
674
689
  'sessions.source.gemini': 'Gemini',
675
690
  'sessions.source.codebuddy': 'CodeBuddy',
691
+
692
+ 'sessions.source.workbuddy': 'WorkBuddy AI',
676
693
  'sessions.source.pi': 'Pi',
694
+ 'sessions.source.opencode': 'OpenCode',
677
695
  'sessions.loadingList': 'セッション一覧を読み込み中...',
678
696
  'sessions.empty': 'セッションがありません',
679
697
  'sessions.unknownTime': '不明な時間',
@@ -685,6 +703,8 @@ const ja = Object.freeze({
685
703
  'sessions.unpin': 'ピン留め解除',
686
704
  'sessions.copyResume': '再開コマンドをコピー',
687
705
  'sessions.preview.refresh': '更新',
706
+ 'sessions.preview.scrollTop': '先頭へ',
707
+ 'sessions.preview.scrollBottom': '末尾へ',
688
708
  'sessions.preview.loading': 'プレビュー読み込み中...',
689
709
  'sessions.batch.select': '一括削除',
690
710
  'sessions.batch.exit': '選択を終了',
@@ -776,7 +796,6 @@ const ja = Object.freeze({
776
796
  'sessions.sort.hot': '話題順',
777
797
  'sessions.sort.hotBadge': '話題',
778
798
  'sessions.filters.copyLink': 'リンクをコピー',
779
- 'sessions.filters.urlBuildFail': 'リンク生成失敗',
780
799
  'sessions.filters.source': 'ソース',
781
800
  'sessions.filters.path': 'パス',
782
801
  'sessions.filters.keyword': 'キーワード',
@@ -2,6 +2,17 @@ const vi = Object.freeze({
2
2
  'plugins.catalog.promptTemplates.title': 'Mẫu prompt',
3
3
  'plugins.catalog.promptTemplates.description': 'Công cụ prompt theo mẫu, hỗ trợ biến, sao chép và xuất.',
4
4
  'plugins.status.standard': 'tiêu chuẩn',
5
+ 'plugins.catalog.textTools.title': 'Công cụ văn bản',
6
+ 'plugins.catalog.textTools.description': 'Bộ công cụ văn bản nhẹ; công cụ đầu tiên nén văn bản nhiều dòng thành một dòng.',
7
+ 'plugins.textTools.title': 'Công cụ văn bản',
8
+ 'plugins.textTools.tools.aria': 'Chọn công cụ văn bản',
9
+ 'plugins.textTools.tools.compress': 'Nén văn bản',
10
+ 'plugins.textTools.compress.inputLabel': 'Nhập văn bản',
11
+ 'plugins.textTools.compress.inputPlaceholder': 'Dán hoặc nhập văn bản cần nén…',
12
+ 'plugins.textTools.compress.inputAria': 'Văn bản cần nén',
13
+ 'plugins.textTools.compress.outputTitle': 'Kết quả nén',
14
+ 'plugins.textTools.compress.outputAria': 'Kết quả văn bản đã nén',
15
+ 'plugins.textTools.compress.copy': 'Sao chép',
5
16
  'plugins.builtin.commentPolish.name': 'Chỉnh chú thích mã',
6
17
  'plugins.builtin.commentPolish.desc': 'Chỉnh nhẹ các chú thích mã sau {{code}}',
7
18
  'plugins.builtin.commentPolish.line1': 'Chỉnh nhẹ các chú thích mã sau',
@@ -272,6 +283,8 @@ const vi = Object.freeze({
272
283
  'prompts.editor.previewEmpty': 'Chưa có nội dung để xem trước',
273
284
  'prompts.editor.changeAxis': 'Trục điều hướng thay đổi',
274
285
  'prompts.editor.changeAxisTick': 'Dòng {line} (+{added} / -{removed})',
286
+ 'prompts.tokenCount.hint': '~{count} token',
287
+ 'prompts.tokenCount.diff': 'Token: trước {before} → hiện tại {current}',
275
288
  'prompts.presets.title': 'Preset',
276
289
  'prompts.presets.hint': 'Quản lý preset.',
277
290
  'prompts.presets.namePlaceholder': 'Tên preset',
@@ -622,6 +635,8 @@ const vi = Object.freeze({
622
635
  'dashboard.sessionSource.claude': 'Claude Code',
623
636
  'dashboard.sessionSource.gemini': 'Gemini CLI',
624
637
  'dashboard.sessionSource.codebuddy': 'CodeBuddy Code',
638
+
639
+ 'dashboard.sessionSource.workbuddy': 'WorkBuddy AI',
625
640
  'dashboard.sessionSource.all': 'Tất cả',
626
641
  'dashboard.sessionPath.all': 'Tất cả đường dẫn',
627
642
  'dashboard.sessionQuery.unsupported': 'Nguồn không được hỗ trợ',
@@ -848,7 +863,10 @@ const vi = Object.freeze({
848
863
  'sessions.source.claudeCode': 'Claude Code',
849
864
  'sessions.source.gemini': 'Gemini CLI',
850
865
  'sessions.source.codebuddy': 'CodeBuddy Code',
866
+
867
+ 'sessions.source.workbuddy': 'WorkBuddy AI',
851
868
  'sessions.source.pi': 'Pi',
869
+ 'sessions.source.opencode': 'OpenCode',
852
870
  'sessions.loadingList': 'Đang tải phiên làm việc...',
853
871
  'sessions.empty': 'Không tìm thấy phiên nào',
854
872
  'sessions.unknownTime': 'thời gian không rõ',
@@ -858,6 +876,8 @@ const vi = Object.freeze({
858
876
  'sessions.unpin': 'Bỏ ghim',
859
877
  'sessions.copyResume': 'Sao chép lệnh tiếp tục',
860
878
  'sessions.preview.refresh': 'Làm mới nội dung',
879
+ 'sessions.preview.scrollTop': 'Về đầu',
880
+ 'sessions.preview.scrollBottom': 'Xuống cuối',
861
881
  'sessions.preview.loading': 'Đang tải...',
862
882
  'sessions.batch.select': 'Xóa hàng loạt',
863
883
  'sessions.batch.exit': 'Thoát chọn',
@@ -947,7 +967,6 @@ const vi = Object.freeze({
947
967
  'sessions.sort.hot': 'Sắp xếp: hot',
948
968
  'sessions.sort.hotBadge': 'Hot',
949
969
  'sessions.filters.copyLink': 'Sao chép link bộ lọc',
950
- 'sessions.filters.urlBuildFail': 'Tạo link thất bại',
951
970
  'sessions.filters.source': 'Nguồn',
952
971
  'sessions.filters.path': 'Đường dẫn',
953
972
  'sessions.filters.keyword': 'Từ khóa',
@@ -254,6 +254,8 @@ const zhTw = Object.freeze({
254
254
  'prompts.editor.previewEmpty': '尚無內容可預覽',
255
255
  'prompts.editor.changeAxis': '改動導覽軸',
256
256
  'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
257
+ 'prompts.tokenCount.hint': '約 {count} tokens',
258
+ 'prompts.tokenCount.diff': 'Tokens:修改前 {before} → 目前 {current}',
257
259
  'prompts.presets.title': '預設池',
258
260
  'prompts.presets.hint': '管理預設。',
259
261
  'prompts.presets.namePlaceholder': '預設名稱',
@@ -350,6 +352,8 @@ const zhTw = Object.freeze({
350
352
  'dashboard.sessionSource.claude': 'Claude Code',
351
353
  'dashboard.sessionSource.gemini': 'Gemini CLI',
352
354
  'dashboard.sessionSource.codebuddy': 'CodeBuddy Code',
355
+
356
+ 'dashboard.sessionSource.workbuddy': 'WorkBuddy AI',
353
357
  'dashboard.sessionSource.all': '全部',
354
358
  'dashboard.sessionPath.all': '全部路徑',
355
359
  'dashboard.sessionQuery.unsupported': '目前來源不支持',
@@ -384,6 +388,17 @@ const zhTw = Object.freeze({
384
388
  'plugins.catalog.promptTemplates.title': '提示詞模板',
385
389
  'plugins.catalog.promptTemplates.description': '標準化、模板驅動的提示詞工具,支持變量、複製與匯出。',
386
390
  'plugins.status.standard': '標準',
391
+ 'plugins.catalog.textTools.title': '文字工具',
392
+ 'plugins.catalog.textTools.description': '一組輕量純文字小工具,首個工具可將多行文字壓縮為單行。',
393
+ 'plugins.textTools.title': '文字工具',
394
+ 'plugins.textTools.tools.aria': '文字工具選擇',
395
+ 'plugins.textTools.tools.compress': '壓縮文字',
396
+ 'plugins.textTools.compress.inputLabel': '輸入文字',
397
+ 'plugins.textTools.compress.inputPlaceholder': '貼上或輸入需要壓縮的文字…',
398
+ 'plugins.textTools.compress.inputAria': '輸入需要壓縮的文字',
399
+ 'plugins.textTools.compress.outputTitle': '壓縮結果',
400
+ 'plugins.textTools.compress.outputAria': '壓縮後的文字結果',
401
+ 'plugins.textTools.compress.copy': '複製',
387
402
  'plugins.main.ariaWorkspace': '插件工作區',
388
403
  'plugins.refresh': '重新整理',
389
404
  'plugins.refreshing': '重新整理中...',
@@ -671,7 +686,10 @@ const zhTw = Object.freeze({
671
686
  'sessions.source.claudeCode': 'Claude Code',
672
687
  'sessions.source.gemini': 'Gemini CLI',
673
688
  'sessions.source.codebuddy': 'CodeBuddy Code',
689
+
690
+ 'sessions.source.workbuddy': 'WorkBuddy AI',
674
691
  'sessions.source.pi': 'Pi',
692
+ 'sessions.source.opencode': 'OpenCode',
675
693
  'sessions.loadingList': '會話載入中...',
676
694
  'sessions.empty': '暫無可用會話記錄',
677
695
  'sessions.unknownTime': '未知時間',
@@ -682,6 +700,8 @@ const zhTw = Object.freeze({
682
700
  'sessions.unpin': '取消置頂',
683
701
  'sessions.copyResume': '複製恢復命令',
684
702
  'sessions.preview.refresh': '重新整理內容',
703
+ 'sessions.preview.scrollTop': '回到頂部',
704
+ 'sessions.preview.scrollBottom': '跳到底部',
685
705
  'sessions.preview.loading': '載入中...',
686
706
  'sessions.batch.select': '批次刪除',
687
707
  'sessions.batch.exit': '退出選擇',
@@ -772,7 +792,6 @@ const zhTw = Object.freeze({
772
792
  'sessions.sort.hot': '按熱度',
773
793
  'sessions.sort.hotBadge': '熱',
774
794
  'sessions.filters.copyLink': '複製篩選鏈接',
775
- 'sessions.filters.urlBuildFail': '無法生成鏈接',
776
795
  'sessions.filters.source': '來源',
777
796
  'sessions.filters.path': '路徑',
778
797
  'sessions.filters.keyword': '關鍵詞',
@@ -254,6 +254,8 @@ const zh = Object.freeze({
254
254
  'prompts.editor.previewEmpty': '暂无内容可预览',
255
255
  'prompts.editor.changeAxis': '改动导航轴',
256
256
  'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
257
+ 'prompts.tokenCount.hint': '约 {count} tokens',
258
+ 'prompts.tokenCount.diff': 'Tokens:修改前 {before} → 当前 {current}',
257
259
  'prompts.presets.title': '预设池',
258
260
  'prompts.presets.hint': '管理预设。',
259
261
  'prompts.presets.namePlaceholder': '预设名称',
@@ -350,6 +352,8 @@ const zh = Object.freeze({
350
352
  'dashboard.sessionSource.claude': 'Claude Code',
351
353
  'dashboard.sessionSource.gemini': 'Gemini CLI',
352
354
  'dashboard.sessionSource.codebuddy': 'CodeBuddy Code',
355
+
356
+ 'dashboard.sessionSource.workbuddy': 'WorkBuddy AI',
353
357
  'dashboard.sessionSource.all': '全部',
354
358
  'dashboard.sessionPath.all': '全部路径',
355
359
  'dashboard.sessionQuery.unsupported': '当前来源不支持',
@@ -384,6 +388,17 @@ const zh = Object.freeze({
384
388
  'plugins.catalog.promptTemplates.title': '提示词模板',
385
389
  'plugins.catalog.promptTemplates.description': '标准化、模板驱动的提示词工具,支持变量、复制与导出。',
386
390
  'plugins.status.standard': '标准',
391
+ 'plugins.catalog.textTools.title': '文本工具',
392
+ 'plugins.catalog.textTools.description': '一组轻量纯文本小工具,首个工具可将多行文本压缩为单行。',
393
+ 'plugins.textTools.title': '文本工具',
394
+ 'plugins.textTools.tools.aria': '文本工具选择',
395
+ 'plugins.textTools.tools.compress': '压缩文本',
396
+ 'plugins.textTools.compress.inputLabel': '输入文本',
397
+ 'plugins.textTools.compress.inputPlaceholder': '粘贴或输入需要压缩的文本…',
398
+ 'plugins.textTools.compress.inputAria': '输入需要压缩的文本',
399
+ 'plugins.textTools.compress.outputTitle': '压缩结果',
400
+ 'plugins.textTools.compress.outputAria': '压缩后的文本结果',
401
+ 'plugins.textTools.compress.copy': '复制',
387
402
  'plugins.main.ariaWorkspace': '插件工作区',
388
403
  'plugins.refresh': '刷新',
389
404
  'plugins.refreshing': '刷新中...',
@@ -671,7 +686,10 @@ const zh = Object.freeze({
671
686
  'sessions.source.claudeCode': 'Claude Code',
672
687
  'sessions.source.gemini': 'Gemini CLI',
673
688
  'sessions.source.codebuddy': 'CodeBuddy Code',
689
+
690
+ 'sessions.source.workbuddy': 'WorkBuddy AI',
674
691
  'sessions.source.pi': 'Pi',
692
+ 'sessions.source.opencode': 'OpenCode',
675
693
  'sessions.loadingList': '会话加载中...',
676
694
  'sessions.empty': '暂无可用会话记录',
677
695
  'sessions.unknownTime': '未知时间',
@@ -682,6 +700,8 @@ const zh = Object.freeze({
682
700
  'sessions.unpin': '取消置顶',
683
701
  'sessions.copyResume': '复制恢复命令',
684
702
  'sessions.preview.refresh': '刷新内容',
703
+ 'sessions.preview.scrollTop': '回到顶部',
704
+ 'sessions.preview.scrollBottom': '跳到底部',
685
705
  'sessions.preview.loading': '加载中...',
686
706
  'sessions.batch.select': '批量删除',
687
707
  'sessions.batch.exit': '退出选择',
@@ -772,7 +792,6 @@ const zh = Object.freeze({
772
792
  'sessions.sort.hot': '按热度',
773
793
  'sessions.sort.hotBadge': '热',
774
794
  'sessions.filters.copyLink': '复制筛选链接',
775
- 'sessions.filters.urlBuildFail': '无法生成链接',
776
795
  'sessions.filters.source': '来源',
777
796
  'sessions.filters.path': '路径',
778
797
  'sessions.filters.keyword': '关键词',
@@ -1,3 +1,9 @@
1
- import { createPluginsComputed } from '../../plugins/prompt-templates/computed.mjs';
1
+ import { createPromptTemplatesComputed } from '../../plugins/prompt-templates/computed.mjs';
2
+ import { createTextToolsComputed } from '../../plugins/text-tools/computed.mjs';
2
3
 
3
- export { createPluginsComputed };
4
+ export function createPluginsComputed() {
5
+ return {
6
+ ...createPromptTemplatesComputed(),
7
+ ...createTextToolsComputed()
8
+ };
9
+ }
@@ -1,3 +1,9 @@
1
- import { createPluginsMethods } from '../../plugins/prompt-templates/methods.mjs';
1
+ import { createPromptTemplatesMethods } from '../../plugins/prompt-templates/methods.mjs';
2
+ import { createTextToolsMethods } from '../../plugins/text-tools/methods.mjs';
2
3
 
3
- export { createPluginsMethods };
4
+ export function createPluginsMethods() {
5
+ return {
6
+ ...createPromptTemplatesMethods(),
7
+ ...createTextToolsMethods()
8
+ };
9
+ }
@@ -289,7 +289,7 @@
289
289
  <div class="side-item-title">{{ t('side.sessions.browser') }}</div>
290
290
  <div class="side-item-meta">
291
291
  <span>{{ t('side.sessions.browser.meta') }}</span>
292
- <span>{{ t('sessions.sourceLabel', { value: (sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'claude' ? 'Claude Code' : (sessionFilterSource === 'gemini' ? 'Gemini CLI' : (sessionFilterSource === 'codebuddy' ? 'CodeBuddy Code' : 'Codex')))) }) }}</span>
292
+ <span>{{ t('sessions.sourceLabel', { value: (sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'claude' ? 'Claude Code' : (sessionFilterSource === 'gemini' ? 'Gemini CLI' : (sessionFilterSource === 'codebuddy' ? 'CodeBuddy Code' : (sessionFilterSource === 'workbuddy' ? 'WorkBuddy AI' : 'Codex'))))) }) }}</span>
293
293
  </div>
294
294
  </button>
295
295
  <button
@@ -346,10 +346,11 @@
346
346
  <button
347
347
  id="side-tab-plugins"
348
348
  data-main-tab="plugins"
349
- :aria-current="mainTab === 'plugins' ? 'page' : null"
350
- :class="['side-item', { active: isMainTabNavActive('plugins') }]"
351
- @pointerdown="onMainTabPointerDown('plugins', $event)"
352
- @click="onMainTabClick('plugins', $event)">
349
+ data-plugins-id="prompt-templates"
350
+ :aria-current="mainTab === 'plugins' && pluginsActiveId === 'prompt-templates' ? 'page' : null"
351
+ :class="['side-item', { active: isPluginsIdNavActive('prompt-templates') }]"
352
+ @pointerdown="onPluginsTabPointerDown('prompt-templates', $event)"
353
+ @click="onPluginsTabClick('prompt-templates', $event)">
353
354
  <span class="side-item-icon" aria-hidden="true">◇</span>
354
355
  <div class="side-item-title">{{ t('side.plugins.tools') }}</div>
355
356
  <div class="side-item-meta">
@@ -357,6 +358,20 @@
357
358
  <span>{{ t('side.plugins.templatesCount', { count: promptTemplatesList.length }) }}</span>
358
359
  </div>
359
360
  </button>
361
+ <button
362
+ id="side-tab-plugins-text-tools"
363
+ data-main-tab="plugins"
364
+ data-plugins-id="text-tools"
365
+ :aria-current="mainTab === 'plugins' && pluginsActiveId === 'text-tools' ? 'page' : null"
366
+ :class="['side-item', { active: isPluginsIdNavActive('text-tools') }]"
367
+ @pointerdown="onPluginsTabPointerDown('text-tools', $event)"
368
+ @click="onPluginsTabClick('text-tools', $event)">
369
+ <span class="side-item-icon" aria-hidden="true">▤</span>
370
+ <div class="side-item-title">{{ t('plugins.catalog.textTools.title') }}</div>
371
+ <div class="side-item-meta">
372
+ <span>{{ t('plugins.textTools.tools.compress') }}</span>
373
+ </div>
374
+ </button>
360
375
  </div>
361
376
 
362
377
  <div class="side-section" role="navigation" :aria-label="t('side.system')">
@@ -512,7 +527,9 @@
512
527
  ? t('sessions.source.gemini')
513
528
  : (sessionFilterSource === 'codebuddy'
514
529
  ? t('sessions.source.codebuddy')
515
- : t('sessions.source.codex'))))) }}
530
+ : (sessionFilterSource === 'workbuddy'
531
+ ? t('sessions.source.workbuddy')
532
+ : t('sessions.source.codex')))))) }}
516
533
  </span>
517
534
  </div>
518
535
  <div class="status-chip">
@@ -11,11 +11,6 @@
11
11
  <span class="selector-title">{{ t('plugins.sidebar.title') }}</span>
12
12
  <div class="plugins-panel-note">{{ t('plugins.sidebar.note') }}</div>
13
13
  </div>
14
- <div class="settings-tab-actions">
15
- <button type="button" class="btn-tool btn-tool-compact" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">
16
- {{ pluginsLoading ? t('plugins.refreshing') : t('plugins.refresh') }}
17
- </button>
18
- </div>
19
14
  </div>
20
15
 
21
16
  <div class="plugins-list" role="list">
@@ -239,6 +234,51 @@
239
234
  </div>
240
235
  </div>
241
236
  </div>
237
+ <div v-else-if="pluginsActiveId === 'text-tools'" class="plugins-panel">
238
+ <div class="plugins-panel-head">
239
+ <div>
240
+ <div class="plugins-panel-title">{{ t('plugins.textTools.title') }}</div>
241
+ <div v-if="pluginsActiveAttribution" class="plugins-panel-note">{{ pluginsActiveAttribution }}</div>
242
+ </div>
243
+ </div>
244
+
245
+ <div class="prompt-templates-modebar" role="tablist" :aria-label="t('plugins.textTools.tools.aria')">
246
+ <button
247
+ v-for="tool in textToolsList"
248
+ :key="'text-tool-' + tool.id"
249
+ type="button"
250
+ role="tab"
251
+ :aria-selected="textToolsActiveToolId === tool.id"
252
+ :class="['mode-pill', { active: textToolsActiveToolId === tool.id }]"
253
+ @click="textToolsActiveToolId = tool.id">{{ tool.label }}</button>
254
+ </div>
255
+
256
+ <div class="prompt-compose">
257
+ <div class="prompt-compose-workspace">
258
+ <div class="prompt-compose-main">
259
+ <div class="prompt-compose-form">
260
+ <label class="form-label">{{ t('plugins.textTools.compress.inputLabel') }}</label>
261
+ <textarea
262
+ class="form-input prompt-editor-textarea"
263
+ v-model="textToolsInput"
264
+ rows="10"
265
+ spellcheck="false"
266
+ :placeholder="t('plugins.textTools.compress.inputPlaceholder')"
267
+ :aria-label="t('plugins.textTools.compress.inputAria')"></textarea>
268
+ </div>
269
+ </div>
270
+ <div class="prompt-preview-block prompt-compose-preview">
271
+ <div class="prompt-vars-head">
272
+ <div>
273
+ <div class="prompt-vars-title">{{ t('plugins.textTools.compress.outputTitle') }}</div>
274
+ </div>
275
+ <button type="button" class="btn-mini" @click="copyTextToolsOutput" :disabled="!textToolsOutput">{{ t('plugins.textTools.compress.copy') }}</button>
276
+ </div>
277
+ <textarea class="form-input prompt-preview-textarea" :value="textToolsOutput" rows="10" readonly spellcheck="false" :aria-label="t('plugins.textTools.compress.outputAria')"></textarea>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ </div>
242
282
 
243
283
  <div v-else class="skills-empty-state">{{ t('plugins.promptTemplates.noPluginSelected') }}</div>
244
284
  </section>
@@ -42,6 +42,7 @@
42
42
  <span v-if="agentsPath">
43
43
  ({{ agentsExists ? t('common.exists') : t('common.notExistsWillCreateOnSave') }})
44
44
  </span>
45
+ <span v-if="agentsPath" class="prompts-token-hint">· {{ t('prompts.tokenCount.hint', { count: agentsTokenCount }) }}</span>
45
46
  </div>
46
47
  <div class="prompts-editor-actions">
47
48
  <div class="prompts-editor-group prompts-editor-group--pathref">
@@ -86,17 +87,9 @@
86
87
  </button>
87
88
  </div>
88
89
  <div class="prompts-editor-group prompts-editor-group--workflow">
89
- <button class="btn-mini" @click="loadPromptsContent" :disabled="agentsSaving || agentsDiffLoading" :title="t('common.cancel')">
90
+ <button class="btn-mini" @click="agentsDiffVisible ? resetAgentsDiffState() : loadPromptsContent()" :disabled="agentsSaving || agentsDiffLoading" :title="t('common.cancel')">
90
91
  <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
91
92
  </button>
92
- <button
93
- v-if="agentsDiffVisible"
94
- class="btn-mini"
95
- @click="resetAgentsDiffState"
96
- :disabled="agentsSaving || agentsDiffLoading"
97
- :title="t('common.backToEdit')">
98
- <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
99
- </button>
100
93
  <button class="btn-mini btn-confirm-mini" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (!agentsDiffVisible && !hasAgentsContentChanged()) || (agentsDiffVisible && !agentsDiffHasChanges)" :title="agentsDiffVisible ? t('common.save') : t('common.preview')">
101
94
  <svg v-if="agentsDiffVisible" class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>
102
95
  <svg v-else class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
@@ -190,6 +183,12 @@
190
183
 
191
184
  <div class="form-group">
192
185
  <div v-if="agentsDiffVisible">
186
+ <div
187
+ v-if="!agentsDiffLoading && !agentsDiffError"
188
+ class="agents-diff-token-summary">
189
+ <span class="agents-diff-token-label">{{ t('prompts.tokenCount.diff', { before: agentsDiffTokenStats.before, current: agentsDiffTokenStats.current }) }}</span>
190
+ <span :class="['agents-diff-token-delta', 'agents-diff-token-delta--' + agentsDiffTokenStats.deltaKind]">{{ agentsDiffTokenStats.deltaLabel }}</span>
191
+ </div>
193
192
  <div
194
193
  v-if="!agentsDiffLoading && !agentsDiffError && !agentsDiffTruncated && (agentsDiffStats.added || agentsDiffStats.removed)"
195
194
  class="agents-diff-summary">
@@ -234,7 +233,6 @@
234
233
  </div>
235
234
  <div class="prompts-editor-columns">
236
235
  <div class="prompts-edit-pane">
237
- <pre ref="promptsAgentsHighlight" class="prompts-highlight-backdrop" aria-hidden="true" v-html="agentsHighlightHtml"></pre>
238
236
  <textarea
239
237
  ref="promptsAgentsTextarea"
240
238
  v-model="agentsContent"
@@ -242,7 +240,6 @@
242
240
  spellcheck="false"
243
241
  :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
244
242
  @input="onAgentsContentInput"
245
- @scroll="syncPromptsOverlayScroll('agents')"
246
243
  :placeholder="t(promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')"></textarea>
247
244
  </div>
248
245
  <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
@@ -284,6 +281,7 @@
284
281
  <span v-if="sysPromptPath">
285
282
  ({{ sysPromptExists ? t('common.exists') : t('common.notExistsWillCreateOnSave') }})
286
283
  </span>
284
+ <span v-if="sysPromptPath" class="prompts-token-hint">· {{ t('prompts.tokenCount.hint', { count: sysPromptTokenCount }) }}</span>
287
285
  </div>
288
286
  <div class="prompts-editor-actions">
289
287
  <div class="prompts-editor-group prompts-editor-group--pathref">
@@ -312,12 +310,9 @@
312
310
  </button>
313
311
  </div>
314
312
  <div class="prompts-editor-group prompts-editor-group--workflow">
315
- <button class="btn-mini" @click="cancelSysPromptEdit" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.cancel')">
313
+ <button class="btn-mini" @click="sysPromptDiffVisible ? resetSysPromptDiffState() : cancelSysPromptEdit()" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.cancel')">
316
314
  <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
317
315
  </button>
318
- <button v-if="sysPromptDiffVisible" class="btn-mini" @click="resetSysPromptDiffState" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.backToEdit')">
319
- <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
320
- </button>
321
316
  <button class="btn-mini btn-confirm-mini" @click="applySystemPrompt" :disabled="sysPromptSaving || sysPromptLoading || sysPromptDiffLoading || (!sysPromptDiffVisible && !hasSysPromptContentChanged()) || (sysPromptDiffVisible && !sysPromptDiffHasChangesValue)" :title="sysPromptDiffVisible ? t('common.save') : t('common.preview')">
322
317
  <svg v-if="sysPromptDiffVisible" class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>
323
318
  <svg v-else class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
@@ -348,6 +343,12 @@
348
343
  </details>
349
344
  <div class="form-group">
350
345
  <div v-if="sysPromptDiffVisible">
346
+ <div
347
+ v-if="!sysPromptDiffLoading && !sysPromptDiffError"
348
+ class="agents-diff-token-summary">
349
+ <span class="agents-diff-token-label">{{ t('prompts.tokenCount.diff', { before: sysPromptDiffTokenStats.before, current: sysPromptDiffTokenStats.current }) }}</span>
350
+ <span :class="['agents-diff-token-delta', 'agents-diff-token-delta--' + sysPromptDiffTokenStats.deltaKind]">{{ sysPromptDiffTokenStats.deltaLabel }}</span>
351
+ </div>
351
352
  <div
352
353
  v-if="!sysPromptDiffLoading && !sysPromptDiffError && !sysPromptDiffTruncated && (sysPromptDiffStats.added || sysPromptDiffStats.removed)"
353
354
  class="agents-diff-summary">
@@ -392,14 +393,12 @@
392
393
  </div>
393
394
  <div class="prompts-editor-columns">
394
395
  <div class="prompts-edit-pane">
395
- <pre ref="promptsSysHighlight" class="prompts-highlight-backdrop" aria-hidden="true" v-html="sysPromptHighlightHtml"></pre>
396
396
  <textarea
397
397
  ref="promptsSysTextarea"
398
398
  v-model="sysPromptContent"
399
399
  class="form-input template-editor"
400
400
  spellcheck="false"
401
401
  :readonly="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible"
402
- @scroll="syncPromptsOverlayScroll('sys')"
403
402
  :placeholder="t('sysPrompt.placeholder')"></textarea>
404
403
  </div>
405
404
  <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">