@bobfrankston/mailx 1.0.435 → 1.0.436
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/client/app.js +50 -7
- package/client/lib/api-client.js +3 -0
- package/client/lib/mailxapi.js +3 -0
- package/package.json +1 -1
- package/packages/mailx-service/index.d.ts +1 -0
- package/packages/mailx-service/index.js +11 -0
- package/packages/mailx-service/jsonrpc.js +2 -0
- package/unwedge.cmd +1 -1
package/client/app.js
CHANGED
|
@@ -2219,7 +2219,7 @@ document.getElementById("btn-open-log")?.addEventListener("click", async () => {
|
|
|
2219
2219
|
}
|
|
2220
2220
|
});
|
|
2221
2221
|
async function openJsoncEditor(initialFile) {
|
|
2222
|
-
const { readJsoncFile, writeJsoncFile, readConfigHelp } = await import("./lib/api-client.js");
|
|
2222
|
+
const { readJsoncFile, writeJsoncFile, readConfigHelp, formatJsonc } = await import("./lib/api-client.js");
|
|
2223
2223
|
const backdrop = document.createElement("div");
|
|
2224
2224
|
backdrop.className = "mailx-modal-backdrop";
|
|
2225
2225
|
const panel = document.createElement("div");
|
|
@@ -2253,6 +2253,7 @@ async function openJsoncEditor(initialFile) {
|
|
|
2253
2253
|
</div>
|
|
2254
2254
|
<div class="mailx-modal-error" id="jsonc-error" hidden></div>
|
|
2255
2255
|
<div class="mailx-modal-buttons">
|
|
2256
|
+
<button type="button" class="mailx-modal-btn" data-action="format" title="Reformat indentation while preserving comments and trailing commas">Format</button>
|
|
2256
2257
|
<span class="mailx-modal-spacer"></span>
|
|
2257
2258
|
<button type="button" class="mailx-modal-btn" data-action="cancel">Cancel</button>
|
|
2258
2259
|
<button type="button" class="mailx-modal-btn mailx-modal-btn-primary" data-action="save">Save</button>
|
|
@@ -2311,17 +2312,34 @@ async function openJsoncEditor(initialFile) {
|
|
|
2311
2312
|
renderGutter();
|
|
2312
2313
|
};
|
|
2313
2314
|
const showValidation = (err) => {
|
|
2314
|
-
|
|
2315
|
+
// CRITICAL: do NOT move the cursor here. Validation fires every 600ms
|
|
2316
|
+
// while the user types; auto-selecting the error position yanked the
|
|
2317
|
+
// cursor mid-edit and made fixing the error impossible (the user
|
|
2318
|
+
// reported this as a fatal bug — the very mechanism preventing a save
|
|
2319
|
+
// was preventing the fix). Location is shown via the gutter highlight
|
|
2320
|
+
// + the "Line N, col M" message, and the user can click "Jump" to
|
|
2321
|
+
// explicitly navigate.
|
|
2322
|
+
errorEl.innerHTML = "";
|
|
2323
|
+
const text = document.createElement("span");
|
|
2324
|
+
text.textContent = `Line ${err.line}, col ${err.col}: ${err.message} `;
|
|
2325
|
+
const jumpBtn = document.createElement("button");
|
|
2326
|
+
jumpBtn.type = "button";
|
|
2327
|
+
jumpBtn.className = "mailx-modal-btn mailx-modal-btn-link";
|
|
2328
|
+
jumpBtn.textContent = "Jump to error";
|
|
2329
|
+
jumpBtn.addEventListener("click", () => {
|
|
2330
|
+
textarea.focus();
|
|
2331
|
+
try {
|
|
2332
|
+
textarea.setSelectionRange(err.pos, err.pos + 1);
|
|
2333
|
+
}
|
|
2334
|
+
catch { /* */ }
|
|
2335
|
+
});
|
|
2336
|
+
errorEl.appendChild(text);
|
|
2337
|
+
errorEl.appendChild(jumpBtn);
|
|
2315
2338
|
errorEl.hidden = false;
|
|
2316
2339
|
textarea.classList.add("mailx-modal-input-error");
|
|
2317
2340
|
saveBtn.disabled = true;
|
|
2318
2341
|
errorLine = err.line;
|
|
2319
2342
|
renderGutter();
|
|
2320
|
-
// Select the problem character so the browser draws a visible marker
|
|
2321
|
-
try {
|
|
2322
|
-
textarea.setSelectionRange(err.pos, err.pos + 1);
|
|
2323
|
-
}
|
|
2324
|
-
catch { /* out-of-range → ignore */ }
|
|
2325
2343
|
};
|
|
2326
2344
|
let validateTimer;
|
|
2327
2345
|
const scheduleValidate = () => {
|
|
@@ -2377,6 +2395,31 @@ async function openJsoncEditor(initialFile) {
|
|
|
2377
2395
|
close();
|
|
2378
2396
|
return;
|
|
2379
2397
|
}
|
|
2398
|
+
if (action === "format") {
|
|
2399
|
+
// Reformat via the service-side jsonc-parser format() — the
|
|
2400
|
+
// edits are whitespace-only, so `//` and `/* */` comments
|
|
2401
|
+
// survive intact (which JSON.stringify(parse(...)) does not).
|
|
2402
|
+
btn.disabled = true;
|
|
2403
|
+
const orig = btn.textContent;
|
|
2404
|
+
btn.textContent = "Formatting…";
|
|
2405
|
+
try {
|
|
2406
|
+
const r = await formatJsonc(textarea.value);
|
|
2407
|
+
if (r?.content !== undefined) {
|
|
2408
|
+
textarea.value = r.content;
|
|
2409
|
+
renderGutter();
|
|
2410
|
+
scheduleValidate();
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
catch (e) {
|
|
2414
|
+
errorEl.textContent = `Format failed: ${e.message}`;
|
|
2415
|
+
errorEl.hidden = false;
|
|
2416
|
+
}
|
|
2417
|
+
finally {
|
|
2418
|
+
btn.disabled = false;
|
|
2419
|
+
btn.textContent = orig || "Format";
|
|
2420
|
+
}
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2380
2423
|
if (action === "save") {
|
|
2381
2424
|
// Final sync-check; refuse to save if it doesn't parse
|
|
2382
2425
|
const err = validateJsonc(textarea.value);
|
package/client/lib/api-client.js
CHANGED
|
@@ -339,6 +339,9 @@ export function readJsoncFile(name) {
|
|
|
339
339
|
export function writeJsoncFile(name, content) {
|
|
340
340
|
return ipc().writeJsoncFile?.(name, content);
|
|
341
341
|
}
|
|
342
|
+
export function formatJsonc(content) {
|
|
343
|
+
return ipc().formatJsonc?.(content);
|
|
344
|
+
}
|
|
342
345
|
export function readConfigHelp(name) {
|
|
343
346
|
return ipc().readConfigHelp?.(name) ?? Promise.resolve({ content: "" });
|
|
344
347
|
}
|
package/client/lib/mailxapi.js
CHANGED
|
@@ -131,6 +131,9 @@
|
|
|
131
131
|
writeJsoncFile: function(name, content) {
|
|
132
132
|
return callNode("writeJsoncFile", { name: name, content: content });
|
|
133
133
|
},
|
|
134
|
+
formatJsonc: function(content) {
|
|
135
|
+
return callNode("formatJsonc", { content: content });
|
|
136
|
+
},
|
|
134
137
|
readConfigHelp: function(name) {
|
|
135
138
|
return callNode("readConfigHelp", { name: name });
|
|
136
139
|
},
|
package/package.json
CHANGED
|
@@ -252,6 +252,7 @@ export declare class MailxService {
|
|
|
252
252
|
* Names are whitelisted so the UI can't read arbitrary files.
|
|
253
253
|
* `config.jsonc` is the local per-machine config (not cloud-synced). */
|
|
254
254
|
readJsoncFile(name: string): Promise<string | null>;
|
|
255
|
+
formatJsonc(content: string): Promise<string>;
|
|
255
256
|
/** Return the help section for a named config file, extracted from docs/config-help.md.
|
|
256
257
|
* Matches a level-2 heading whose text equals the filename. Returns markdown. */
|
|
257
258
|
readConfigHelp(name: string): Promise<string>;
|
|
@@ -1762,6 +1762,17 @@ export class MailxService {
|
|
|
1762
1762
|
const { cloudRead } = await import("@bobfrankston/mailx-settings");
|
|
1763
1763
|
return cloudRead(name);
|
|
1764
1764
|
}
|
|
1765
|
+
// Reformat JSONC preserving comments — applyEdits returns whitespace-only edits.
|
|
1766
|
+
async formatJsonc(content) {
|
|
1767
|
+
const { format, applyEdits } = await import("jsonc-parser");
|
|
1768
|
+
const edits = format(content, undefined, {
|
|
1769
|
+
tabSize: 2,
|
|
1770
|
+
insertSpaces: true,
|
|
1771
|
+
eol: "\n",
|
|
1772
|
+
insertFinalNewline: true,
|
|
1773
|
+
});
|
|
1774
|
+
return applyEdits(content, edits);
|
|
1775
|
+
}
|
|
1765
1776
|
/** Return the help section for a named config file, extracted from docs/config-help.md.
|
|
1766
1777
|
* Matches a level-2 heading whose text equals the filename. Returns markdown. */
|
|
1767
1778
|
async readConfigHelp(name) {
|
|
@@ -169,6 +169,8 @@ async function dispatchAction(svc, action, p) {
|
|
|
169
169
|
case "writeJsoncFile":
|
|
170
170
|
await svc.writeJsoncFile(p.name, p.content);
|
|
171
171
|
return { ok: true };
|
|
172
|
+
case "formatJsonc":
|
|
173
|
+
return { content: await svc.formatJsonc(p.content) };
|
|
172
174
|
case "readConfigHelp":
|
|
173
175
|
return { content: await svc.readConfigHelp(p.name) };
|
|
174
176
|
case "unsubscribeOneClick":
|
package/unwedge.cmd
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
rmdir C:\Users\Bob\.claude\session-env\
|
|
1
|
+
rmdir C:\Users\Bob\.claude\session-env\6787e337-1af0-423c-ae58-8d981702aebb /s /q
|