Turns the empty scaffold into a working Nova extension for Apple Container and container-compose, in the shape of the Docker Suite extension. Sidebar (Scripts/Sidebar): - System, Containers, Images, Volumes and Networks sections - Containers grouped into compose projects via the com.docker.compose.project label container-compose stamps; project rows drive compose up/down/build against the matching workspace file - Lifecycle, shell, logs, inspect, browse and copy commands per resource - Apple Container publishes no event stream, so the sidebar polls while visible and only reloads a section when a fingerprint of its data changes, which keeps selection and expansion intact Hostnames (Scripts/Hostnames.js): - A container's hostname is <name>.<domain> and the domain lives in Apple Container's config.toml, which has no CLI setter, so the Hostname Domain preference reads and writes that file directly - Changing it offers to register the domain with macOS and restart the services; the write preserves the file's other tables and its mode Language support: - Dockerfile and Compose syntaxes backed by tree-sitter grammars built by Tools/build-syntaxes.sh, pinned to revisions that generate ABI 14 - The Compose syntax is named dockercompose because that is the languageId docker-language-server recognises compose files by - docker-language-server is downloaded on first use rather than bundled, at roughly 40 MB Icons are generated by Tools/make-icons.py so they can be regenerated rather than hand-maintained. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UnSBcR5Lywz5Fbj5FmVbfc
62 lines
1.6 KiB
JavaScript
62 lines
1.6 KiB
JavaScript
//
|
|
// Runs commands in the user's terminal application.
|
|
//
|
|
// Nova has no terminal of its own, so anything interactive or long running --
|
|
// shells, followed logs, compose output -- is handed to Terminal or iTerm2
|
|
// through AppleScript.
|
|
//
|
|
|
|
const { exec } = require("../Utilities/Process");
|
|
|
|
class Terminal {
|
|
// Quotes a single argument for /bin/sh.
|
|
static quote(argument) {
|
|
return `'${String(argument).replace(/'/g, `'\\''`)}'`;
|
|
}
|
|
|
|
// Joins an argument vector into a shell command.
|
|
static command(parts) {
|
|
return parts.map((part) => this.quote(part)).join(" ");
|
|
}
|
|
|
|
static escapeAppleScript(text) {
|
|
return text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
}
|
|
|
|
// Opens a new terminal window running the given shell command.
|
|
static async run(command, cwd) {
|
|
let full = cwd ? `cd ${this.quote(cwd)} && ${command}` : command;
|
|
let script =
|
|
nova.config.get("apple_container.cli.terminal", "string") === "iTerm2"
|
|
? this.iTermScript(full)
|
|
: this.terminalScript(full);
|
|
|
|
return exec("/usr/bin/osascript", ["-e", script]);
|
|
}
|
|
|
|
static terminalScript(command) {
|
|
let escaped = this.escapeAppleScript(command);
|
|
return [
|
|
'tell application "Terminal"',
|
|
` do script "${escaped}"`,
|
|
" activate",
|
|
"end tell"
|
|
].join("\n");
|
|
}
|
|
|
|
static iTermScript(command) {
|
|
let escaped = this.escapeAppleScript(command);
|
|
return [
|
|
'tell application "iTerm"',
|
|
" activate",
|
|
" set newWindow to (create window with default profile)",
|
|
" tell current session of newWindow",
|
|
` write text "${escaped}"`,
|
|
" end tell",
|
|
"end tell"
|
|
].join("\n");
|
|
}
|
|
}
|
|
|
|
module.exports = { Terminal };
|