@fresh-editor/fresh-editor 0.3.7 → 0.3.9
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/CHANGELOG.md +97 -0
- package/README.md +0 -1
- package/package.json +1 -1
- package/plugins/audit_mode.i18n.json +28 -0
- package/plugins/audit_mode.ts +34 -3
- package/plugins/config-schema.json +7 -0
- package/plugins/dashboard.ts +3 -3
- package/plugins/env-manager.ts +168 -0
- package/plugins/examples/bookmarks.ts +3 -2
- package/plugins/git_log.ts +58 -75
- package/plugins/lib/finder.ts +57 -10
- package/plugins/lib/fresh.d.ts +150 -3
- package/plugins/lib/widgets.ts +8 -0
- package/plugins/live_diff.ts +12 -17
- package/plugins/live_grep.i18n.json +349 -27
- package/plugins/live_grep.ts +596 -141
- package/plugins/orchestrator.ts +1506 -534
- package/plugins/pkg.ts +168 -3
- package/plugins/schemas/theme.schema.json +53 -14
- package/plugins/theme_editor.i18n.json +84 -84
- package/plugins/tsconfig.json +1 -0
- package/plugins/vi_mode.ts +3 -3
- package/themes/light.json +1 -1
- package/themes/terminal.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,102 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## 0.3.9
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
#### Universal Search (multi-scope Live Grep)
|
|
8
|
+
|
|
9
|
+
Live Grep grows into a universal search overlay: search across multiple **scopes** — project files, open **Buffers**, and **Terminals** scrollback — with **Word** and **Regex** search modes (each keybindable). The overlay gets a clickable widget toolbar (focusable provider button, toggle controls), a full-width header band, and inline status shown in the overlay itself. Closed-terminal scrollback is retained so it stays searchable (#2099).
|
|
10
|
+
|
|
11
|
+
#### Orchestrator & git worktrees
|
|
12
|
+
|
|
13
|
+
* **Attach sessions to existing git worktrees** and discover them automatically (#2095).
|
|
14
|
+
* Multi-select **bulk actions**, worktree ordering, and a *Show all worktrees* filter toggle.
|
|
15
|
+
* Draggable scrollbars on the picker lists, with bulk-pane reorder.
|
|
16
|
+
|
|
17
|
+
#### Editor
|
|
18
|
+
|
|
19
|
+
* **Move to Next / Previous Paragraph** actions, including translations (#2084, contributed by @PavelLoparev; requested in #2083).
|
|
20
|
+
|
|
21
|
+
#### Language Support
|
|
22
|
+
|
|
23
|
+
* **C3** (`.c3`) syntax highlighting and LSP (#2101, requested by @data-man).
|
|
24
|
+
|
|
25
|
+
#### Plugins & API
|
|
26
|
+
|
|
27
|
+
* New `getWorkingDataDir()` (per-working-dir data root) and `getTerminalDir()` plugin APIs.
|
|
28
|
+
* Overlay toolbar widget APIs — clickable `Toggle`/`Button` controls, `toggleOverlayToolbarWidget`, and a `Row { wrap }` layout that reflows toolbars across lines.
|
|
29
|
+
|
|
30
|
+
### Improvements
|
|
31
|
+
|
|
32
|
+
* The **dashboard no longer opens automatically** on startup by default. To re-enable it, open **Settings**, select **Plugin: dashboard**, and turn on **AutoOpen** ("Show the dashboard automatically when Fresh starts with no real files open."). The dashboard remains available any time via the **Show Dashboard** command.
|
|
33
|
+
* Homebrew install simplified now that fresh-editor is in homebrew-core — `brew install fresh-editor`, no tap step.
|
|
34
|
+
|
|
35
|
+
### Bug Fixes
|
|
36
|
+
|
|
37
|
+
* Plugin `getCursorLine()` now returns the real cursor line instead of `0` (#2076, reported by @pmburov).
|
|
38
|
+
* **Reduced serial-console lag** by not repainting on non-visual plugin async events (#2100, reported by @jetpax).
|
|
39
|
+
* **Custom theme colours** no longer silently ignored for many UI fields (#2080, contributed by @flexiondotorg; reported in #2079).
|
|
40
|
+
* `PageDown`/`PageUp` no longer overshoot on a single soft-wrapped line (#2085).
|
|
41
|
+
* **Workspace restore** no longer mixes tabs from multiple projects in one window (#2056, reported by @mandolyte).
|
|
42
|
+
* **Live Grep**: *Resume* keeps the query and opens the selected result; overlay preview and input-box undo/redo corrected; Buffers scope finds unmodified open buffers; non-file-scope previews and read-only data-dir files fixed.
|
|
43
|
+
* `getKeybindingLabel` resolves correctly for bound plugin actions.
|
|
44
|
+
|
|
45
|
+
### Internal
|
|
46
|
+
|
|
47
|
+
* Major window refactor: window-scoped save/restore (`Window::from_workspace`), `WindowId`-parameterized persistence, `working_dir` derived from the active window, and `open_file`/LSP/watch helpers moved onto `Window`.
|
|
48
|
+
* Orchestrator bring-up characterization tests and fixtures across persistence layouts.
|
|
49
|
+
* Serial-console lag benchmark/diagnostic scripts; theme-key resolver schema-drift guard.
|
|
50
|
+
* Internal docs excluded from the public docs build.
|
|
51
|
+
|
|
52
|
+
## 0.3.8
|
|
53
|
+
|
|
54
|
+
### Features / Improvements
|
|
55
|
+
|
|
56
|
+
#### Environment Managers (venv / direnv / mise)
|
|
57
|
+
|
|
58
|
+
New built-in **environment-manager** plugin: it detects a project's environment manager — `.venv`/`venv`, `.envrc` (direnv), or `mise.toml`/`.tool-versions` — and, via **Env: Activate**, injects that environment into every editor-spawned process (LSP, formatters, terminals, `spawnProcess`). **Env: Use System (Deactivate)** restores the system environment, and **Env: Show Status** reports the current state. Activation is on-demand (not automatic) and respects Workspace Trust. Adds an opt-in `env` status-bar element.
|
|
59
|
+
|
|
60
|
+
#### Remote Development
|
|
61
|
+
|
|
62
|
+
* **LSP over SSH** now runs the language server *on the remote host* instead of falling back to a host-local process, so completions and diagnostics reflect the remote toolchain.
|
|
63
|
+
|
|
64
|
+
#### Workspace Trust (groundwork)
|
|
65
|
+
|
|
66
|
+
Foundational **Workspace Trust** support — a per-project trust level (persisted in the project state dir), a *Set Workspace Trust Level* command-palette control, and a `workspaceTrustLevel()` plugin API. The enforcement gate is **off by default this release** (no prompt on open; undecided folders are treated as Trusted) while the trust UX is reworked around sandboxed execution — so there is no behavior change unless you set a trust level yourself.
|
|
67
|
+
|
|
68
|
+
#### Orchestrator
|
|
69
|
+
|
|
70
|
+
* The **Open** dialog is now scoped to the current project by default, with a visible scope toggle to reach other projects, a tabular picker, and `/` to filter.
|
|
71
|
+
* New windows open atomically with their terminal (no `[No Name]` placeholder), and terminal output shows immediately.
|
|
72
|
+
|
|
73
|
+
#### Settings UI
|
|
74
|
+
|
|
75
|
+
A broad pass on the Settings editor: **number fields now accept direct typing** instead of `[-]`/`[+]` spinners; list editing gets inline `[+] Add new` / `[x]` rows, mouse support, and `Del` to remove; **`Ctrl+R` resets a field to its default**; the focused field's description is shown above the dialog buttons; and confirmations are required before deleting or discarding dirty edits.
|
|
76
|
+
|
|
77
|
+
#### Editor
|
|
78
|
+
|
|
79
|
+
* **Next / Previous Window** commands to cycle open windows without the Switch-Project picker (#2031).
|
|
80
|
+
* **Move File Explorer to Other Side** command, persisted to config (#1468, requested by @asukaminato0721).
|
|
81
|
+
* **Templ** (`.templ`) syntax highlighting (#463, requested by @TS22082).
|
|
82
|
+
* New `editor.confirm_quit` setting (default off) to prompt before quitting a clean session.
|
|
83
|
+
|
|
84
|
+
#### Plugins & API
|
|
85
|
+
|
|
86
|
+
* Built-in `editor.httpFetch(url, path)` for downloading files — `curl` is no longer bundled.
|
|
87
|
+
* "Install from URL" accepts direct `file://` URLs.
|
|
88
|
+
|
|
89
|
+
### Bug Fixes
|
|
90
|
+
|
|
91
|
+
* **Quit binding overrides honored** (#2030): binding `Ctrl+Q` to `noop`/`none` now actually disables it in every context.
|
|
92
|
+
* **Terminals**: line-number gutter and current-line highlight no longer leak on terminal exit; scroll-back viewport anchors correctly; `Shift+Tab` is forwarded to the child as `ESC[Z` (#2029).
|
|
93
|
+
* **File explorer keeps keyboard focus** when leaving a live terminal (#2029).
|
|
94
|
+
* **Closed terminals no longer reappear**, and buffer groups stay visible when a split closes (#2027, reported by @SolarLune).
|
|
95
|
+
* **Git Log**: selected commit stays aligned with the cursor; selection follows scrolling and row clicks.
|
|
96
|
+
* **Adjacent tab indicators** no longer double up (#1997, reported by @brunnerh).
|
|
97
|
+
* **Theming / Live Diff**: light-theme dim fixes, markdown popup body text inherits the terminal fg, and diff lines preserve syntax foreground.
|
|
98
|
+
* Tree-sitter `.scm` query files are included in the Nix source filter, fixing a source-build failure (#2055, reported by @melekbadreddine).
|
|
99
|
+
|
|
3
100
|
## 0.3.7
|
|
4
101
|
|
|
5
102
|
Introduces an experimental **multi-window orchestrator** command (`Alt+Q`) - a UI that manages multiple Fresh windows side by side in a single Fresh process.
|
package/README.md
CHANGED
|
@@ -84,7 +84,6 @@ On macOS and some linux distros (Bazzite/Bluefin/Aurora):
|
|
|
84
84
|
> **Note:** On macOS, see [macOS Terminal Tips](https://getfresh.dev/docs/configuration/keyboard#macos-terminal-tips) for recommended terminal configuration.
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
brew tap sinelaw/fresh
|
|
88
87
|
brew install fresh-editor
|
|
89
88
|
```
|
|
90
89
|
|
package/package.json
CHANGED
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
"cmd.export_json": "Review: Export to JSON",
|
|
39
39
|
"cmd.export_json_desc": "Export review to .review/session.json",
|
|
40
40
|
"status.refreshing": "Refreshing review diff...",
|
|
41
|
+
"status.refreshing_range": "Refreshing %{range}... (working tree not included)",
|
|
42
|
+
"status.working_tree_not_included": "working tree not included",
|
|
41
43
|
"status.updated": "Review diff updated. Found %{count} hunks.",
|
|
42
44
|
"status.loading_diff": "Loading side-by-side diff...",
|
|
43
45
|
"status.not_git_repo": "Not in a git repository",
|
|
@@ -120,6 +122,8 @@
|
|
|
120
122
|
"cmd.export_json": "Revize: Exportovat do JSON",
|
|
121
123
|
"cmd.export_json_desc": "Exportovat revizi do .review/session.json",
|
|
122
124
|
"status.refreshing": "Obnovuji rozdily revize...",
|
|
125
|
+
"status.refreshing_range": "Obnovuji %{range}... (pracovni strom neni zahrnut)",
|
|
126
|
+
"status.working_tree_not_included": "pracovni strom neni zahrnut",
|
|
123
127
|
"status.updated": "Rozdily revize aktualizovany. Nalezeno %{count} bloku.",
|
|
124
128
|
"status.loading_diff": "Nacitam rozdily vedle sebe...",
|
|
125
129
|
"status.not_git_repo": "Neni v git repozitari",
|
|
@@ -202,6 +206,8 @@
|
|
|
202
206
|
"cmd.export_json": "Review: Als JSON exportieren",
|
|
203
207
|
"cmd.export_json_desc": "Review nach .review/session.json exportieren",
|
|
204
208
|
"status.refreshing": "Unterschiede werden aktualisiert...",
|
|
209
|
+
"status.refreshing_range": "Aktualisiere %{range}... (Arbeitsbaum nicht enthalten)",
|
|
210
|
+
"status.working_tree_not_included": "Arbeitsbaum nicht enthalten",
|
|
205
211
|
"status.updated": "Unterschiede aktualisiert. %{count} Blocke gefunden.",
|
|
206
212
|
"status.loading_diff": "Nebeneinander-Ansicht wird geladen...",
|
|
207
213
|
"status.not_git_repo": "Kein Git-Repository",
|
|
@@ -284,6 +290,8 @@
|
|
|
284
290
|
"cmd.export_json": "Revision: Exportar a JSON",
|
|
285
291
|
"cmd.export_json_desc": "Exportar revision a .review/session.json",
|
|
286
292
|
"status.refreshing": "Actualizando diferencias de revision...",
|
|
293
|
+
"status.refreshing_range": "Actualizando %{range}... (arbol de trabajo no incluido)",
|
|
294
|
+
"status.working_tree_not_included": "arbol de trabajo no incluido",
|
|
287
295
|
"status.updated": "Diferencias actualizadas. Se encontraron %{count} bloques.",
|
|
288
296
|
"status.loading_diff": "Cargando diferencias lado a lado...",
|
|
289
297
|
"status.not_git_repo": "No esta en un repositorio git",
|
|
@@ -366,6 +374,8 @@
|
|
|
366
374
|
"cmd.export_json": "Revue: Exporter en JSON",
|
|
367
375
|
"cmd.export_json_desc": "Exporter la revue vers .review/session.json",
|
|
368
376
|
"status.refreshing": "Actualisation des differences de revue...",
|
|
377
|
+
"status.refreshing_range": "Actualisation de %{range}... (arbre de travail non inclus)",
|
|
378
|
+
"status.working_tree_not_included": "arbre de travail non inclus",
|
|
369
379
|
"status.updated": "Differences mises a jour. %{count} blocs trouves.",
|
|
370
380
|
"status.loading_diff": "Chargement des differences cote a cote...",
|
|
371
381
|
"status.not_git_repo": "Pas dans un depot git",
|
|
@@ -448,6 +458,8 @@
|
|
|
448
458
|
"cmd.export_json": "Revisione: Esporta in JSON",
|
|
449
459
|
"cmd.export_json_desc": "Esporta la revisione in .review/session.json",
|
|
450
460
|
"status.refreshing": "Aggiornamento differenze revisione...",
|
|
461
|
+
"status.refreshing_range": "Aggiornamento %{range}... (albero di lavoro non incluso)",
|
|
462
|
+
"status.working_tree_not_included": "albero di lavoro non incluso",
|
|
451
463
|
"status.updated": "Differenze revisione aggiornate. Trovati %{count} blocchi.",
|
|
452
464
|
"status.loading_diff": "Caricamento diff affiancato...",
|
|
453
465
|
"status.not_git_repo": "Non in un repository git",
|
|
@@ -530,6 +542,8 @@
|
|
|
530
542
|
"cmd.export_json": "レビュー: JSONにエクスポート",
|
|
531
543
|
"cmd.export_json_desc": "レビューを.review/session.jsonにエクスポート",
|
|
532
544
|
"status.refreshing": "レビュー差分を更新中...",
|
|
545
|
+
"status.refreshing_range": "%{range}を更新中... (ワーキングツリーは含まれません)",
|
|
546
|
+
"status.working_tree_not_included": "ワーキングツリーは含まれません",
|
|
533
547
|
"status.updated": "レビュー差分を更新しました。%{count}個のハンクが見つかりました。",
|
|
534
548
|
"status.loading_diff": "サイドバイサイド差分を読み込み中...",
|
|
535
549
|
"status.not_git_repo": "gitリポジトリではありません",
|
|
@@ -612,6 +626,8 @@
|
|
|
612
626
|
"cmd.export_json": "검토: JSON으로 내보내기",
|
|
613
627
|
"cmd.export_json_desc": "리뷰를 .review/session.json으로 내보내기",
|
|
614
628
|
"status.refreshing": "리뷰 차이점 새로고침 중...",
|
|
629
|
+
"status.refreshing_range": "%{range} 새로고침 중... (작업 트리 미포함)",
|
|
630
|
+
"status.working_tree_not_included": "작업 트리 미포함",
|
|
615
631
|
"status.updated": "리뷰 차이점이 업데이트되었습니다. %{count}개의 헝크를 찾았습니다.",
|
|
616
632
|
"status.loading_diff": "나란히 비교 로딩 중...",
|
|
617
633
|
"status.not_git_repo": "git 저장소가 아닙니다",
|
|
@@ -694,6 +710,8 @@
|
|
|
694
710
|
"cmd.export_json": "Revisao: Exportar para JSON",
|
|
695
711
|
"cmd.export_json_desc": "Exportar revisao para .review/session.json",
|
|
696
712
|
"status.refreshing": "Atualizando diferencas de revisao...",
|
|
713
|
+
"status.refreshing_range": "Atualizando %{range}... (arvore de trabalho nao incluida)",
|
|
714
|
+
"status.working_tree_not_included": "arvore de trabalho nao incluida",
|
|
697
715
|
"status.updated": "Diferencas de revisao atualizadas. Encontrados %{count} blocos.",
|
|
698
716
|
"status.loading_diff": "Carregando diferencas lado a lado...",
|
|
699
717
|
"status.not_git_repo": "Nao esta em um repositorio git",
|
|
@@ -776,6 +794,8 @@
|
|
|
776
794
|
"cmd.export_json": "Ревью: Экспорт в JSON",
|
|
777
795
|
"cmd.export_json_desc": "Экспортировать ревью в .review/session.json",
|
|
778
796
|
"status.refreshing": "Обновление изменений ревью...",
|
|
797
|
+
"status.refreshing_range": "Обновление %{range}... (рабочее дерево не включено)",
|
|
798
|
+
"status.working_tree_not_included": "рабочее дерево не включено",
|
|
779
799
|
"status.updated": "Изменения ревью обновлены. Найдено %{count} блоков.",
|
|
780
800
|
"status.loading_diff": "Загрузка сравнения бок о бок...",
|
|
781
801
|
"status.not_git_repo": "Не в git-репозитории",
|
|
@@ -858,6 +878,8 @@
|
|
|
858
878
|
"cmd.export_json": "ตรวจสอบ: ส่งออกเป็น JSON",
|
|
859
879
|
"cmd.export_json_desc": "ส่งออกการตรวจสอบไปยัง .review/session.json",
|
|
860
880
|
"status.refreshing": "กำลังรีเฟรชความแตกต่างการตรวจสอบ...",
|
|
881
|
+
"status.refreshing_range": "กำลังรีเฟรช %{range}... (ไม่รวม working tree)",
|
|
882
|
+
"status.working_tree_not_included": "ไม่รวม working tree",
|
|
861
883
|
"status.updated": "อัปเดตความแตกต่างการตรวจสอบแล้ว พบ %{count} บล็อก",
|
|
862
884
|
"status.loading_diff": "กำลังโหลดการเปรียบเทียบแบบเคียงข้าง...",
|
|
863
885
|
"status.not_git_repo": "ไม่อยู่ใน git repository",
|
|
@@ -940,6 +962,8 @@
|
|
|
940
962
|
"cmd.export_json": "Рев'ю: Експорт у JSON",
|
|
941
963
|
"cmd.export_json_desc": "Експортувати рев'ю до .review/session.json",
|
|
942
964
|
"status.refreshing": "Оновлення змін рев'ю...",
|
|
965
|
+
"status.refreshing_range": "Оновлення %{range}... (робоче дерево не включено)",
|
|
966
|
+
"status.working_tree_not_included": "робоче дерево не включено",
|
|
943
967
|
"status.updated": "Зміни рев'ю оновлено. Знайдено %{count} блоків.",
|
|
944
968
|
"status.loading_diff": "Завантаження порівняння поруч...",
|
|
945
969
|
"status.not_git_repo": "Не в git-репозиторії",
|
|
@@ -1022,6 +1046,8 @@
|
|
|
1022
1046
|
"cmd.export_json": "Xem xét: Xuất ra JSON",
|
|
1023
1047
|
"cmd.export_json_desc": "Xuất xem xét ra .review/session.json",
|
|
1024
1048
|
"status.refreshing": "Đang làm mới khác biệt xem xét...",
|
|
1049
|
+
"status.refreshing_range": "Đang làm mới %{range}... (không bao gồm cây làm việc)",
|
|
1050
|
+
"status.working_tree_not_included": "không bao gồm cây làm việc",
|
|
1025
1051
|
"status.updated": "Đã cập nhật khác biệt xem xét. Tìm thấy %{count} khối.",
|
|
1026
1052
|
"status.loading_diff": "Đang tải so sánh song song...",
|
|
1027
1053
|
"status.not_git_repo": "Không trong kho git",
|
|
@@ -1104,6 +1130,8 @@
|
|
|
1104
1130
|
"cmd.export_json": "审查: 导出为JSON",
|
|
1105
1131
|
"cmd.export_json_desc": "将审查导出到.review/session.json",
|
|
1106
1132
|
"status.refreshing": "正在刷新审查差异...",
|
|
1133
|
+
"status.refreshing_range": "正在刷新 %{range}... (不包含工作树)",
|
|
1134
|
+
"status.working_tree_not_included": "不包含工作树",
|
|
1107
1135
|
"status.updated": "审查差异已更新。找到%{count}个代码块。",
|
|
1108
1136
|
"status.loading_diff": "正在加载并排差异...",
|
|
1109
1137
|
"status.not_git_repo": "不在git仓库中",
|
package/plugins/audit_mode.ts
CHANGED
|
@@ -1743,7 +1743,30 @@ function applyCursorLineOverlay(panel: 'diff'): void {
|
|
|
1743
1743
|
});
|
|
1744
1744
|
}
|
|
1745
1745
|
|
|
1746
|
-
function review_refresh() {
|
|
1746
|
+
function review_refresh() {
|
|
1747
|
+
// Synchronously acknowledge the keypress before kicking off the
|
|
1748
|
+
// async `git status` + `git diff`. Those calls take long enough on
|
|
1749
|
+
// a non-trivial repo that, without this immediate status update,
|
|
1750
|
+
// the sticky-header totals visibly lag the new content: users
|
|
1751
|
+
// press `r`, see the old `+N / -M`, conclude the keystroke was
|
|
1752
|
+
// dropped, and press `r` again — which then "appears" to work
|
|
1753
|
+
// because the first refresh has by then landed. See #2036.
|
|
1754
|
+
//
|
|
1755
|
+
// In range mode the refresh is intentionally a no-op for
|
|
1756
|
+
// working-tree edits (the diff is always between two refs); the
|
|
1757
|
+
// range-specific message explains that up front so the user
|
|
1758
|
+
// doesn't think `r` is broken when their unstaged changes don't
|
|
1759
|
+
// show up.
|
|
1760
|
+
if (state.mode === 'range' && state.range) {
|
|
1761
|
+
editor.setStatus(
|
|
1762
|
+
editor.t("status.refreshing_range", { range: state.range.label }) ||
|
|
1763
|
+
`Refreshing ${state.range.label}... (working tree not included)`
|
|
1764
|
+
);
|
|
1765
|
+
} else {
|
|
1766
|
+
editor.setStatus(editor.t("status.refreshing") || "Refreshing review diff...");
|
|
1767
|
+
}
|
|
1768
|
+
void refreshMagitData();
|
|
1769
|
+
}
|
|
1747
1770
|
registerHandler("review_refresh", review_refresh);
|
|
1748
1771
|
|
|
1749
1772
|
// --- Cursor-driven navigation ---
|
|
@@ -3163,13 +3186,21 @@ function updateReviewStatus(): void {
|
|
|
3163
3186
|
if (state.groupId === null) return;
|
|
3164
3187
|
const total = state.hunkHeaderRows.length;
|
|
3165
3188
|
const current = currentGlobalHunkIndex();
|
|
3189
|
+
// Range reviews fundamentally don't include working-tree edits; the
|
|
3190
|
+
// suffix makes that visible from the status bar at all times rather
|
|
3191
|
+
// than only flashing past during a refresh. Without it users hit `r`,
|
|
3192
|
+
// see their unsaved changes don't appear, and conclude the refresh is
|
|
3193
|
+
// broken (#2036).
|
|
3194
|
+
const rangeNote = state.mode === 'range' && state.range
|
|
3195
|
+
? ` · ${editor.t("status.working_tree_not_included") || "working tree not included"}`
|
|
3196
|
+
: '';
|
|
3166
3197
|
if (current !== null) {
|
|
3167
3198
|
editor.setStatus(editor.t("status.review_summary_indexed", {
|
|
3168
3199
|
current: String(current),
|
|
3169
3200
|
count: String(total),
|
|
3170
|
-
}));
|
|
3201
|
+
}) + rangeNote);
|
|
3171
3202
|
} else {
|
|
3172
|
-
editor.setStatus(editor.t("status.review_summary", { count: String(total) }));
|
|
3203
|
+
editor.setStatus(editor.t("status.review_summary", { count: String(total) }) + rangeNote);
|
|
3173
3204
|
}
|
|
3174
3205
|
}
|
|
3175
3206
|
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"auto_save_enabled": false,
|
|
104
104
|
"auto_save_interval_secs": 30,
|
|
105
105
|
"hot_exit": true,
|
|
106
|
+
"confirm_quit": false,
|
|
106
107
|
"restore_previous_session": true,
|
|
107
108
|
"skip_session_restore_when_files_passed": true,
|
|
108
109
|
"auto_create_empty_buffer_on_last_buffer_close": true,
|
|
@@ -655,6 +656,12 @@
|
|
|
655
656
|
"default": true,
|
|
656
657
|
"x-section": "Recovery"
|
|
657
658
|
},
|
|
659
|
+
"confirm_quit": {
|
|
660
|
+
"description": "Whether to confirm before quitting Fresh. When enabled, pressing\nthe quit shortcut surfaces a confirmation prompt even when no\nbuffers are modified. Useful for users who frequently hit\n`Ctrl+Q` by mistake. Independent of the unsaved-changes prompt,\nwhich always fires regardless of this setting.\nDefault: false",
|
|
661
|
+
"type": "boolean",
|
|
662
|
+
"default": false,
|
|
663
|
+
"x-section": "Startup"
|
|
664
|
+
},
|
|
658
665
|
"restore_previous_session": {
|
|
659
666
|
"description": "Whether to auto-open previously opened files (session restore) when\nstarting Fresh in a directory. When enabled (the default), tabs,\nsplits, cursor positions and the file explorer state are restored\nfrom the last clean exit in the same working directory. When\ndisabled, Fresh starts with a clean workspace. The workspace file\non disk is still written on exit, so re-enabling this setting picks\nup whatever state was saved at the most recent clean exit. The\n`--no-restore` CLI flag is a stronger override: it skips both\nrestoring and saving the workspace.\nDefault: true",
|
|
660
667
|
"type": "boolean",
|
package/plugins/dashboard.ts
CHANGED
|
@@ -16,7 +16,7 @@ const editor = getEditor();
|
|
|
16
16
|
// it is never loaded, so no buffers are created, no timers run,
|
|
17
17
|
// and no network fetches fire.
|
|
18
18
|
//
|
|
19
|
-
// A second flag `plugins.dashboard.auto-open` (default
|
|
19
|
+
// A second flag `plugins.dashboard.auto-open` (default false) gates
|
|
20
20
|
// only the ambient open paths (startup + last-buffer-closed). When
|
|
21
21
|
// false the plugin still loads and the "Show Dashboard" command is
|
|
22
22
|
// still available — it just won't appear on its own.
|
|
@@ -1590,7 +1590,7 @@ registerHandler("dashboardShowOrFocus", dashboardShowOrFocus);
|
|
|
1590
1590
|
// comes from the typed plugin-config field declared below. The field
|
|
1591
1591
|
// shows up in the Settings UI under "Plugin Settings → dashboard".
|
|
1592
1592
|
editor.defineConfigBoolean("autoOpen", {
|
|
1593
|
-
default:
|
|
1593
|
+
default: false,
|
|
1594
1594
|
description: "Show the dashboard automatically when Fresh starts with no real files open.",
|
|
1595
1595
|
});
|
|
1596
1596
|
|
|
@@ -1599,7 +1599,7 @@ let autoOpenOverride: boolean | null = null;
|
|
|
1599
1599
|
function autoOpenEnabled(): boolean {
|
|
1600
1600
|
if (autoOpenOverride !== null) return autoOpenOverride;
|
|
1601
1601
|
const cfg = (editor.getPluginConfig() ?? {}) as { autoOpen?: boolean };
|
|
1602
|
-
return cfg.autoOpen
|
|
1602
|
+
return cfg.autoOpen === true;
|
|
1603
1603
|
}
|
|
1604
1604
|
|
|
1605
1605
|
function shouldShowDashboard(): boolean {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Environment Manager
|
|
5
|
+
*
|
|
6
|
+
* Detects a project's environment manager (Python venv, direnv, mise) and
|
|
7
|
+
* activates it by handing core an activation **snippet** via `editor.setEnv`.
|
|
8
|
+
* Core captures the resulting environment on the active backend (local / SSH)
|
|
9
|
+
* and applies it to every editor-spawned process — language servers,
|
|
10
|
+
* formatters, `spawnProcess`.
|
|
11
|
+
*
|
|
12
|
+
* Detection is passive (reads files only). Activation runs repo-controlled
|
|
13
|
+
* code, so it is gated on Workspace Trust: the plugin only calls `setEnv` when
|
|
14
|
+
* `editor.workspaceTrustLevel() === "trusted"` (and core enforces the same).
|
|
15
|
+
*
|
|
16
|
+
* Freshness: one-shot spawns re-capture automatically when the env inputs
|
|
17
|
+
* change (core's cache is keyed on them). A long-running language server has
|
|
18
|
+
* its env fixed at spawn, so to pick up a changed `.envrc`/`mise.toml` the
|
|
19
|
+
* user runs **Env: Reload**, which re-captures and restarts servers. (Auto
|
|
20
|
+
* file-watching is intentionally not wired yet.)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const editor = getEditor();
|
|
24
|
+
|
|
25
|
+
const STATUS_TOKEN = "env";
|
|
26
|
+
|
|
27
|
+
interface Detected {
|
|
28
|
+
/** Short label for the status pill, e.g. ".venv" / "direnv" / "mise". */
|
|
29
|
+
name: string;
|
|
30
|
+
/** The activation snippet handed to `editor.setEnv`. */
|
|
31
|
+
snippet: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function fileExists(p: string): boolean {
|
|
35
|
+
try {
|
|
36
|
+
return editor.fileExists(p);
|
|
37
|
+
} catch (_e) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Detect the environment in the current workspace and return its activation
|
|
44
|
+
* snippet, or null if none. These are auto-detected default snippets; direnv
|
|
45
|
+
* and mise need their exporters (they're prompt-hook driven), venv sources its
|
|
46
|
+
* activate script, and anything else is a pure login shell / user snippet.
|
|
47
|
+
*/
|
|
48
|
+
function detect(): Detected | null {
|
|
49
|
+
const cwd = editor.getCwd();
|
|
50
|
+
if (!cwd) return null;
|
|
51
|
+
|
|
52
|
+
for (const name of [".venv", "venv"]) {
|
|
53
|
+
const dir = editor.pathJoin(cwd, name);
|
|
54
|
+
if (
|
|
55
|
+
fileExists(editor.pathJoin(dir, "bin", "python")) ||
|
|
56
|
+
fileExists(editor.pathJoin(dir, "bin", "python3")) ||
|
|
57
|
+
fileExists(editor.pathJoin(dir, "Scripts", "python.exe"))
|
|
58
|
+
) {
|
|
59
|
+
return { name, snippet: `source ${editor.pathJoin(dir, "bin", "activate")}` };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (fileExists(editor.pathJoin(cwd, ".envrc"))) {
|
|
64
|
+
return { name: "direnv", snippet: `eval "$(direnv export bash)"` };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const name of ["mise.toml", ".mise.toml", ".tool-versions"]) {
|
|
68
|
+
if (fileExists(editor.pathJoin(cwd, name))) {
|
|
69
|
+
return { name: "mise", snippet: `eval "$(mise env -s bash)"` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isTrusted(): boolean {
|
|
77
|
+
return editor.workspaceTrustLevel() === "trusted";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// === Commands ===
|
|
81
|
+
|
|
82
|
+
/** Activate (or, when already active, reload) the detected environment. */
|
|
83
|
+
function activate(): void {
|
|
84
|
+
if (!isTrusted()) {
|
|
85
|
+
editor.setStatus(
|
|
86
|
+
"Workspace not trusted — run “Workspace Trust: Trust This Folder” to activate the environment",
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const det = detect();
|
|
91
|
+
if (!det) {
|
|
92
|
+
editor.setStatus("No environment manager detected in this project");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Core captures `snippet` on the active backend and applies it to every
|
|
96
|
+
// spawn; it restarts so language servers re-spawn under the fresh env.
|
|
97
|
+
editor.setEnv(det.snippet, editor.getCwd());
|
|
98
|
+
editor.setStatus(
|
|
99
|
+
`${editor.envActive() ? "Reloading" : "Activating"} ${det.name} environment…`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
registerHandler("env_activate_handler", activate);
|
|
103
|
+
|
|
104
|
+
function useSystem(): void {
|
|
105
|
+
editor.clearEnv();
|
|
106
|
+
editor.setStatus("Environment deactivated — using the system environment");
|
|
107
|
+
}
|
|
108
|
+
registerHandler("env_use_system_handler", useSystem);
|
|
109
|
+
|
|
110
|
+
function showStatus(): void {
|
|
111
|
+
const det = detect();
|
|
112
|
+
const trust = editor.workspaceTrustLevel() || "unavailable";
|
|
113
|
+
if (editor.envActive()) {
|
|
114
|
+
editor.setStatus(`Environment active${det ? ` (${det.name})` : ""}`);
|
|
115
|
+
} else if (det) {
|
|
116
|
+
editor.setStatus(
|
|
117
|
+
`Detected ${det.name} (trust: ${trust}). Run “Env: Activate” to use it.`,
|
|
118
|
+
);
|
|
119
|
+
} else {
|
|
120
|
+
editor.setStatus(`No environment detected (trust: ${trust})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
registerHandler("env_status_handler", showStatus);
|
|
124
|
+
|
|
125
|
+
editor.registerCommand(
|
|
126
|
+
"env_activate",
|
|
127
|
+
"Env: Activate Detected Environment (venv / direnv / mise)",
|
|
128
|
+
"env_activate_handler",
|
|
129
|
+
);
|
|
130
|
+
editor.registerCommand(
|
|
131
|
+
"env_reload",
|
|
132
|
+
"Env: Reload Environment (re-capture after .envrc/mise.toml change)",
|
|
133
|
+
"env_activate_handler",
|
|
134
|
+
);
|
|
135
|
+
editor.registerCommand(
|
|
136
|
+
"env_use_system",
|
|
137
|
+
"Env: Use System (Deactivate Environment)",
|
|
138
|
+
"env_use_system_handler",
|
|
139
|
+
);
|
|
140
|
+
editor.registerCommand(
|
|
141
|
+
"env_status",
|
|
142
|
+
"Env: Show Environment Status",
|
|
143
|
+
"env_status_handler",
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// === Status pill (opt-in to a user's status-bar layout) ===
|
|
147
|
+
|
|
148
|
+
function refreshStatus(): void {
|
|
149
|
+
const bufferId = editor.getActiveBufferId();
|
|
150
|
+
if (bufferId === 0) return;
|
|
151
|
+
const det = detect();
|
|
152
|
+
let value: string;
|
|
153
|
+
if (editor.envActive()) {
|
|
154
|
+
value = det ? `${det.name} ✓` : "active";
|
|
155
|
+
} else {
|
|
156
|
+
value = det ? `${det.name}${isTrusted() ? "" : " (locked)"}` : "system";
|
|
157
|
+
}
|
|
158
|
+
editor.setStatusBarValue(bufferId, STATUS_TOKEN, value);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
editor.registerStatusBarElement(STATUS_TOKEN, "Environment");
|
|
162
|
+
|
|
163
|
+
registerHandler("env_refresh_status", refreshStatus);
|
|
164
|
+
for (const event of ["buffer_activated", "after_file_open", "focus_gained"]) {
|
|
165
|
+
editor.on(event, "env_refresh_status");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
refreshStatus();
|
|
@@ -46,8 +46,9 @@ function getCurrentLocation(): {
|
|
|
46
46
|
|
|
47
47
|
// Helper: Get actual line number using the API
|
|
48
48
|
function getCurrentLineCol(): { line: number; column: number } {
|
|
49
|
-
//
|
|
50
|
-
|
|
49
|
+
// Read the primary cursor's line from the snapshot. `line` is null when the
|
|
50
|
+
// buffer has no line index yet (huge files); fall back to 0 there.
|
|
51
|
+
const lineNumber = editor.getPrimaryCursor()?.line ?? 0;
|
|
51
52
|
|
|
52
53
|
// Get cursor position within the line by reading buffer content
|
|
53
54
|
const bufferId = editor.getActiveBufferId();
|