Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion builder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const fs = require("fs");
const os = require("os");
const path = require("path");
const { MIME_TYPES, ALL_FILES } = require("./mime");
const { installerScript, columnsOf, MAX_COLUMNS, ROWS } = require("./installer");
const { installerScript, columnsOf, MAX_COLUMNS, ROWS, MAX_LABEL } = require("./installer");

/** What an app's config extends: the kit's base config, by the package's name. */
const KIT_BASE = "@diamonddigitaldev/electron-kit/builder/base.json";
Expand Down Expand Up @@ -89,6 +89,9 @@ function checkContextMenu(contextMenu) {
if (typeof contextMenu.label !== "string" || contextMenu.label.trim() === "" || /["\r\n]/.test(contextMenu.label)) {
throw new Error("config(): contextMenu.label must be the entry's text, such as \"Convert with App\", on one line, without quotes.");
}
if (contextMenu.label.length > MAX_LABEL) {
throw new Error(`config(): contextMenu.label is ${contextMenu.label.length} characters; the installer's line holds ${MAX_LABEL}. Shorten it, such as "Convert with App".`);
}
if (contextMenu.folders !== undefined && typeof contextMenu.folders !== "boolean") throw new Error("config(): contextMenu.folders must be true or false.");
if (contextMenu.args !== undefined && (!Array.isArray(contextMenu.args) || !contextMenu.args.every((arg) => typeof arg === "string" && /^--[a-z0-9][a-z0-9-]*(=[A-Za-z0-9._-]+)?$/.test(arg)))) {
throw new Error("config(): contextMenu.args must be switches, such as [\"--upload\"].");
Expand Down
12 changes: 9 additions & 3 deletions builder/installer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
const MAX_COLUMNS = 4;
const ROWS = 8;

/**
* The longest right-click label the page's line holds: 'Add "<label>" to the
* right-click menu' wraps past about ninety characters, and a checkbox one line
* high shows the middle of two lines, clipped top and bottom.
*/
const MAX_LABEL = 48;

/** A string inside NSIS's double quotes. */
function nsisString(text) {
return String(text)
Expand Down Expand Up @@ -169,9 +176,8 @@ function installerScript({ productName, description = "", fileTypes, contextMenu
);
}
if (contextMenu) {
const where = contextMenu.folders ? "files and folders" : "files";
lines.push(
` \${NSD_CreateCheckbox} 0u ${exts.length > 0 ? 129 : 0}u 100% 10u "Add $\\"${controlText(contextMenu.label)}$\\" to the menu when you right-click ${where}"`,
` \${NSD_CreateCheckbox} 0u ${exts.length > 0 ? 129 : 0}u 100% 10u "Add $\\"${controlText(contextMenu.label)}$\\" to the right-click menu"`,
" Pop $KitMenuBox",
' ${if} $KitMenu == "1"',
" ${NSD_Check} $KitMenuBox",
Expand Down Expand Up @@ -347,4 +353,4 @@ function installerScript({ productName, description = "", fileTypes, contextMenu
return lines.join("\r\n") + "\r\n";
}

module.exports = { installerScript, columnsOf, nsisString, MAX_COLUMNS, ROWS };
module.exports = { installerScript, columnsOf, nsisString, MAX_COLUMNS, ROWS, MAX_LABEL };
3 changes: 2 additions & 1 deletion docs/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ contextMenu: { label: "Convert with Diamond File Converter", folders: true },

`contextMenu` is the app's entry on the menu Explorer shows when a file is right-clicked: its `label`,
`folders: true` to show it on folders too, and `args`, switches given after the file (Dropgate's
`["--upload"]`).
`["--upload"]`). The installer's box for it reads `Add "<label>" to the right-click menu` on one line, so a
`label` holds at most 48 characters.

### The Asking Installer (Windows)

Expand Down
5 changes: 3 additions & 2 deletions test/builder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ test("config() refuses what would claim types without asking, or leave the .desk
[[PKG, { build: BUILD, contextMenu: "Convert" }], /contextMenu must be \{ label/],
[[PKG, { build: BUILD, contextMenu: { label: "" } }], /contextMenu\.label must be/],
[[PKG, { build: BUILD, contextMenu: { label: 'Open "it"' } }], /contextMenu\.label must be/],
[[PKG, { build: BUILD, contextMenu: { label: "Convert with ".padEnd(49, "x") } }], /contextMenu\.label is 49 characters; the installer's line holds 48/],
[[PKG, { build: BUILD, contextMenu: { label: "Open", verb: "open" } }], /contextMenu\.verb isn't an option/],
[[PKG, { build: BUILD, contextMenu: { label: "Open", folders: "yes" } }], /contextMenu\.folders must be/],
[[PKG, { build: BUILD, contextMenu: { label: "Open", args: ["upload"] } }], /contextMenu\.args must be switches/],
Expand Down Expand Up @@ -229,7 +230,7 @@ test("the installer: a box per file type in its group's column, all ticked unles
assert.ok(script.includes(`WriteRegStr SHELL_CONTEXT "\${INSTALL_REGISTRY_KEY}" "KitFileType.${ext}" "$KitType${i}"`), ext);
});
assert.match(script, /"Tick All"[\s\S]*"Untick All"/);
assert.match(script, /"Add \$\\"Convert with Diamond File Converter\$\\" to the menu when you right-click files and folders"/);
assert.match(script, /"Add \$\\"Convert with Diamond File Converter\$\\" to the right-click menu"/);
assert.match(script, /\$\{GetOptions\} \$R0 "\/FILETYPES=" \$R1/);
assert.match(script, /\$\{GetOptions\} \$R0 "\/NOCONTEXTMENU" \$R1/);
// An update (--updated) never shows it.
Expand Down Expand Up @@ -276,7 +277,7 @@ test("the installer: an app that takes any file asks only about its right-click
assert.doesNotMatch(script, /Choose File Types|KitListHas|KitType|FILETYPES|RegisteredApplications|Directory\\shell/);
assert.match(script, /WriteRegStr SHELL_CONTEXT "Software\\Classes\\\*\\shell\\DropgateClient\\command" "" '"\$appExe" "%1" --upload'/);
assert.match(script, /WriteRegStr SHELL_CONTEXT "Software\\Classes\\\*\\shell\\DropgateClient" "MultiSelectModel" "Player"/);
assert.match(script, /"Add \$\\"Share with Dropgate\$\\" to the menu when you right-click files"/);
assert.match(script, /"Add \$\\"Share with Dropgate\$\\" to the right-click menu"/);
});

test("the installer: text from the app is kept as text in NSIS's strings", () => {
Expand Down
Loading