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
108 lines
2.2 KiB
JavaScript
108 lines
2.2 KiB
JavaScript
//
|
|
// Formatting helpers shared by the tree items.
|
|
//
|
|
|
|
const UNITS = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
|
|
function bytes(value) {
|
|
if (typeof value !== "number" || !isFinite(value) || value < 0) {
|
|
return "";
|
|
}
|
|
|
|
let index = 0;
|
|
let size = value;
|
|
|
|
while (size >= 1000 && index < UNITS.length - 1) {
|
|
size = size / 1000;
|
|
index += 1;
|
|
}
|
|
|
|
let precision = size >= 100 || index === 0 ? 0 : 1;
|
|
return `${size.toFixed(precision)} ${UNITS[index]}`;
|
|
}
|
|
|
|
// Relative age, in the same spirit as Docker's "2 hours ago".
|
|
function age(value) {
|
|
if (!value) {
|
|
return "";
|
|
}
|
|
|
|
let date = value instanceof Date ? value : new Date(value);
|
|
|
|
if (isNaN(date.getTime()) || date.getTime() === 0) {
|
|
return "";
|
|
}
|
|
|
|
let seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));
|
|
|
|
if (seconds < 60) {
|
|
return "just now";
|
|
}
|
|
|
|
let steps = [
|
|
["minute", 60],
|
|
["hour", 60],
|
|
["day", 24],
|
|
["week", 7],
|
|
["month", 4.35],
|
|
["year", 12]
|
|
];
|
|
|
|
let amount = seconds;
|
|
let label = "second";
|
|
|
|
for (let [name, divisor] of steps) {
|
|
if (amount < divisor) {
|
|
break;
|
|
}
|
|
amount = amount / divisor;
|
|
label = name;
|
|
}
|
|
|
|
let rounded = Math.floor(amount);
|
|
return `${rounded} ${label}${rounded === 1 ? "" : "s"} ago`;
|
|
}
|
|
|
|
// Splits an image reference into its registry, repository and tag.
|
|
function reference(value) {
|
|
let text = String(value || "");
|
|
let digest = null;
|
|
let tag = null;
|
|
|
|
let atIndex = text.lastIndexOf("@");
|
|
if (atIndex > 0) {
|
|
digest = text.slice(atIndex + 1);
|
|
text = text.slice(0, atIndex);
|
|
}
|
|
|
|
let colonIndex = text.lastIndexOf(":");
|
|
let slashIndex = text.lastIndexOf("/");
|
|
if (colonIndex > slashIndex) {
|
|
tag = text.slice(colonIndex + 1);
|
|
text = text.slice(0, colonIndex);
|
|
}
|
|
|
|
let registry = null;
|
|
let repository = text;
|
|
let firstSlash = text.indexOf("/");
|
|
|
|
if (firstSlash > 0) {
|
|
let candidate = text.slice(0, firstSlash);
|
|
if (candidate.includes(".") || candidate.includes(":") || candidate === "localhost") {
|
|
registry = candidate;
|
|
repository = text.slice(firstSlash + 1);
|
|
}
|
|
}
|
|
|
|
return {
|
|
registry: registry,
|
|
repository: repository,
|
|
tag: tag || "latest",
|
|
digest: digest,
|
|
// docker.io/library/nginx reads better as nginx.
|
|
short: repository.replace(/^library\//, "")
|
|
};
|
|
}
|
|
|
|
module.exports = { bytes, age, reference };
|