-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmessages.ts
More file actions
214 lines (185 loc) · 9.03 KB
/
messages.ts
File metadata and controls
214 lines (185 loc) · 9.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/**
* Centralized user-facing message catalog.
*
* Every string shown to the user (toasts, dialogs, inline errors) should come
* from here so we have a single source of truth that is easy to translate later.
*
* Templates use `{param}` placeholders, resolved at call time by `msg()`.
*/
const messages = {
// ── Git: general ──────────────────────────────────────────
"git.commandFailed": "Git {command} failed: {detail}",
// ── Git: branch / switch ──────────────────────────────────
"git.switch.dirtyWorktree": "Cannot switch branches — commit or stash your changes first",
// ── Git: commit ───────────────────────────────────────────
"git.commit.failed": "Commit failed: {detail}",
"git.commit.hookFailed": "Pre-commit hook failed",
"git.hook.failed": "{hook} hook failed",
// ── Git: push / sync ──────────────────────────────────────
"git.push.failed": "Push failed: {detail}",
"git.sync.failed": "Sync failed: {detail}",
// ── Git: merge ────────────────────────────────────────────
"git.merge.failed": "Merge failed",
"git.merge.conflicts": "Merge has conflicts",
"git.merge.conflictsDetail": "Merge has conflicts:\n{files}",
"git.merge.finishFailed": "Could not complete the merge",
"git.merge.abortFailed": "Could not abort the merge: {detail}",
// ── Git: pull from source ─────────────────────────────────
"git.pull.failed": "Pull failed: {detail}",
"git.pull.localChanges": "Local changes need to be stashed before pulling from {branch}",
"git.pull.reapplyConflicts": "Re-applying local changes has conflicts",
"git.pull.stashPreserved":
"Pull did not complete. Your local changes remain in a Lightcode stash.",
// ── Git: worktree ─────────────────────────────────────────
"git.worktree.noBranch": "Cannot create a default worktree path without a branch name",
"git.worktree.dirtySource":
"Branch '{branch}' has uncommitted changes in '{path}' — commit or stash them before merging",
"git.worktree.cleanupFailed": "{original}\nWorktree cleanup also failed: {cleanup}",
// ── Git: WSL ──────────────────────────────────────────────
"git.wsl.homeNotFound": 'Unable to resolve home directory for WSL distro "{distro}"',
"git.wsl.mkdirFailed": 'Unable to create WSL worktree directory "{path}"',
// ── Git: PR ───────────────────────────────────────────────
"git.pr.createFailed": "Failed to create pull request: {detail}",
"git.pr.mergeFailed": "Failed to merge pull request: {detail}",
"git.pr.closeFailed": "Failed to close pull request: {detail}",
// ── Git: generate message ─────────────────────────────────
"git.generateMessage.failed": "Could not generate commit message: {detail}",
// ── Supervisor ────────────────────────────────────────────
"supervisor.restarted": "Background process restarted",
"supervisor.exited": "Background process exited unexpectedly",
"supervisor.notRunning": "Background process is not running",
// ── App update ────────────────────────────────────────────
"update.error": "Update error: {detail}",
} as const;
// ---------------------------------------------------------------------------
/** Union of every known message key. */
export type MessageKey = keyof typeof messages;
/**
* Look up a user-facing message by key with optional `{param}` interpolation.
*
* ```ts
* msg("git.merge.conflictsDetail", { files: "src/index.ts\nREADME.md" })
* // → "Merge has conflicts:\nsrc/index.ts\nREADME.md"
* ```
*/
export function msg(key: MessageKey, params?: Record<string, string | number>): string {
let text: string = messages[key];
if (params) {
for (const [k, v] of Object.entries(params)) {
text = text.replaceAll(`{${k}}`, String(v));
}
}
return text;
}
/**
* Extract the raw message string from an unknown caught value.
*
* Use this for `console.error` logging or when you need the unmodified detail.
* For user-facing toasts, prefer {@link friendlyError} instead.
*/
export function errorDetail(err: unknown): string {
return err instanceof Error ? err.message : String(err);
}
// ---------------------------------------------------------------------------
// Pattern → friendly message mapping (for user-facing toasts)
// ---------------------------------------------------------------------------
/**
* Each entry is a regex tested against the raw error string and the
* message key (+ optional param extractor) to use when it matches.
* Order matters — first match wins.
*/
const errorPatterns: Array<{
test: RegExp;
key: MessageKey;
params?: (raw: string) => Record<string, string>;
}> = [
{
test: /local changes.*would be overwritten/i,
key: "git.switch.dirtyWorktree",
},
{
test: /not fully merged/i,
key: "git.merge.failed",
},
{
test: /CONFLICT|Merge conflict/,
key: "git.merge.conflicts",
},
];
/** Strip Electron IPC wrapper noise from error messages. */
function stripIpcPrefix(raw: string): string {
return raw.replace(/^Error invoking remote method '[^']+': Error:\s*/i, "");
}
/**
* Sentinel used to ferry an extra "details" block (typically full stderr)
* inside the single-string error channel between supervisor → main → renderer.
* Null bytes do not appear in legitimate error messages, so the marker is
* collision-safe against real content.
*/
const DETAILS_SENTINEL = "