~philipwhite/nix-package-search

b5a93efbbc739dcf6dbe170acdd6f934925a8833 — Philip White 2 years ago 2a85f84
Add a lot of features from the official site

Added styles, expand/collapse, and all the fields in the expanded details
view. The details view also has links for both the license and maintainer's
github account (if included).
4 files changed, 193 insertions(+), 22 deletions(-)

A index.css
M index.html
M index.js
A split.js
A index.css => index.css +32 -0
@@ 0,0 1,32 @@
body {
	font-family: Helvetica;
	width: px;
	margin: 20px 3em;
}
table {
	width: 100%;
}

tr {
	padding: 8px;
	font-size: 0.8rem;
}

th {
	text-align: left;
	min-width: max-content;
}

th,
td {
	padding: 8px;
}

tr.result > th,
tr.result > td {
	border-top: 1px solid #ddd;
}

tr.odd.result > td {
	background-color: #f5f5f5;
}

M index.html => index.html +3 -1
@@ 5,6 5,7 @@
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Search NixOS packages</title>
		<link rel="stylesheet" type="text/css" href="index.css" />
	</head>
	<body>
		<h1>Search NixOS packages</h1>


@@ 17,7 18,8 @@
			id="filter-input"
			placeholder="search by name or description"
		/>
		<ul id="packages-list"></ul>
		<table id="packages-list"></table>

		<script src="index.js"></script>
	</body>
</html>

M index.js => index.js +141 -21
@@ 4,27 4,121 @@ console.log("hello world");

// The json object returned from the AJAX request - this is only loaded once per
// page load.
let channel_data = null;
let channel_data = {};

// This is the list of packages that gets displayed on the screen. All major
// work should happen in the background; when it is finished it should update
// this variable and refresh the package list.
let current_packages = {};
let current_expansion = null;

let header_row = document.createElement("tr");
let pn_header = document.createElement("th");
pn_header.innerHTML = "Package name";
header_row.appendChild(pn_header);
let an_header = document.createElement("th");
an_header.innerHTML = "Attribute name";
header_row.appendChild(an_header);
let d_header = document.createElement("th");
d_header.innerHTML = "Description";
header_row.appendChild(d_header);

let not_specified_string = `<i>Not specified</i>`;
let details_fields = [
	{
		get_value: (meta, package_key) => `nix-env -iA nixos.${package_key}`,
		display: "Install command"
	},
	{ get_value: meta => meta.position, display: "Nix expression" },
	{ get_value: meta => meta.platforms, display: "Platforms" },
	{ get_value: meta => meta.homepage, display: "Homepage" },
	{
		get_value: meta => {
			if (meta.license && meta.license.url) {
				return `<a href="${meta.license.url}">${meta.license.fullName}</a>`;
			} else if (meta.license) {
				return meta.license.fullName;
			} else {
				return undefined;
			}
		},
		display: "License"
	},
	{
		get_value: meta => {
			if (meta.maintainers) {
				let maintainer_strings = [];
				for (let maintainer of meta.maintainers) {
					let name_string = `${maintainer.name} &lt;${maintainer.email}&gt;`;
					if (maintainer.github) {
						maintainer_strings.push(
							`<a href="https://github.com/${maintainer.github}">${name_string}</a>`
						);
					} else {
						maintainer_strings.push[name_string];
					}
				}
				return maintainer_strings.join(", ");
			} else {
				return undefined;
			}
		},
		display: "Maintainers"
	},
	{ get_value: meta => meta.longDescription, display: "Long description" }
];

function insert_details(package_item, even_or_odd_class, package, package_key) {
	let details = document.getElementById("details-row");
	if (details) {
		details.parentNode.removeChild(details);
	}

	if (current_expansion !== package_key) {
		current_expansion = package_key;

		let row = document.createElement("tr");
		row.id = "details-row";
		let cell = document.createElement("td");
		let details = document.createElement("div");
		cell.setAttribute("colspan", 3);
		row.appendChild(cell);
		cell.appendChild(details);

		let table = document.createElement("table");
		for (let field of details_fields) {
			let tr = document.createElement("tr");
			tr.classList.add(even_or_odd_class);
			let th = document.createElement("th");
			let td = document.createElement("td");
			th.innerHTML = field.display;
			td.innerHTML = field.get_value(package.meta, package_key) || not_specified_string;
			tr.appendChild(th);
			tr.appendChild(td);
			table.appendChild(tr);
		}
		details.appendChild(table);
		packages_list.insertBefore(row, package_item.nextSibling);
	} else {
		current_expansion = null;
	}
}

function refresh_list() {
	if (channel_data !== null) {
		current_packages = {};
		let package_count = 0;
		for (let package_key in channel_data.packages) {
			let package = channel_data.packages[package_key];
			let filter_items = filter_input.value.split(" ");
		for (let package_key in channel_data) {
			let package = channel_data[package_key];
			let filter_items = filter_input.value.toLowerCase().split(" ");
			let passed = true;
			for (let filter_item of filter_items) {
				if (
					!(
						package.name.includes(filter_item) ||
						package_key.includes(filter_item) ||
						(package.meta.discription && package.meta.description.includes(filter_item))
						package.name.toLowerCase().includes(filter_item) ||
						package_key.toLowerCase().includes(filter_item) ||
						(package.meta.discription &&
							package.meta.description.toLowerCase().includes(filter_item))
					)
				) {
					passed = false;


@@ 35,33 129,59 @@ function refresh_list() {
				current_packages[package_key] = package;
				package_count++;
			}
			if (package_count > 200) {
			if (package_count > 100) {
				break;
			}
		}
		packages_list.innerHTML = "";
		packages_list.appendChild(header_row);
		let index = 0;
		for (let package_key in current_packages) {
			let package = current_packages[package_key];
			let package_div = document.createElement("li");
			package_div.innerHTML = `${package.name} - ${package_key} - ${package.meta.description}`;
			let package_div = document.createElement("tr");
			package_div.classList.add("result");
			let even_or_odd_class = "odd";
			if (index % 2 === 0) {
				even_or_odd_class = "even";
			}
			package_div.classList.add(even_or_odd_class);
			let package_name_div = document.createElement("td");
			let package_key_div = document.createElement("td");
			let package_description_div = document.createElement("td");
			package_name_div.innerHTML = package.name;
			package_key_div.innerHTML = package_key;
			if (package.meta.description) {
				package_description_div.innerHTML = package.meta.description;
			}
			package_div.appendChild(package_name_div);
			package_div.appendChild(package_key_div);
			package_div.appendChild(package_description_div);
			packages_list.appendChild(package_div);
			package_div.addEventListener("click", function() {
				insert_details(package_div, even_or_odd_class, package, package_key);
			});
			index++;
		}
	}
}

let request = new XMLHttpRequest();
request.onreadystatechange = function() {
	if (request.readyState === 4 && request.status === 200) {
		channel_data = JSON.parse(request.responseText);
		console.log("got the data");
		refresh_list();
	}
};

filter_input.addEventListener("keyup", function() {
	console.log(filter_input.value);
	refresh_list();
});

request.open("GET", "packages.json");
request.send(null);
for (let i of [0, 1, 2, 3, 4]) {
	let request = new XMLHttpRequest();
	request.onreadystatechange = function() {
		if (request.readyState === 4 && request.status === 200) {
			let json_data = JSON.parse(request.responseText);
			for (let package_key in json_data) {
				channel_data[package_key] = json_data[package_key];
			}
			refresh_list();
		}
	};

	request.open("GET", `packages.${i}.json`);
	request.send(null);
}

A split.js => split.js +17 -0
@@ 0,0 1,17 @@
const fs = require("fs");

let contents = JSON.parse(fs.readFileSync("packages.json"));

let file_index = 0;
let new_file_contents = {};
let count = 0;
for (let package_key in contents.packages) {
	new_file_contents[package_key] = contents.packages[package_key];
	count++;
	if (count > 10000) {
		count = 0;
		fs.writeFileSync(`packages.${file_index}.json`, JSON.stringify(new_file_contents));
		file_index++;
		new_file_contents = {};
	}
}