diff --git a/.changeset/little-light-bulb.md b/.changeset/little-light-bulb.md
new file mode 100644
index 00000000..798b4d96
--- /dev/null
+++ b/.changeset/little-light-bulb.md
@@ -0,0 +1,6 @@
+---
+"@nodesecure/vis-network": minor
+---
+
+Visualize highlited packages with dashed yellow border
+
\ No newline at end of file
diff --git a/README.md b/README.md
index f0e81faf..bb40f9bb 100644
--- a/README.md
+++ b/README.md
@@ -151,6 +151,7 @@ Type a package name directly to search, or prefix with a filter name followed by
- `ext` — file extension present in the package (e.g. `.js`, `.ts`).
- `builtin` — Node.js core module used by the package (e.g. `fs`, `path`).
- `size` — size range (see [size-satisfies](https://github.com/NodeSecure/size-satisfies#usage-example), e.g. `>50kb`, `10kb..200kb`).
+- `highlighted` — all highlighted packages by default.
## FAQ
diff --git a/i18n/arabic.js b/i18n/arabic.js
index ae8889dd..7375d722 100644
--- a/i18n/arabic.js
+++ b/i18n/arabic.js
@@ -243,7 +243,8 @@ const ui = {
legend: {
default: "الحزمة بخير.",
warn: "الحزمة بها تحذيرات.",
- friendly: "الحزمة تتم صيانتها بواسطة نفس مؤلفي الحزمة الجذرية."
+ friendly: "الحزمة تتم صيانتها بواسطة نفس مؤلفي الحزمة الجذرية.",
+ highlighted: "الحزمة جزء من الحزم المميزة"
},
lockedNavigation: {
next: "التالي",
diff --git a/i18n/english.js b/i18n/english.js
index 01f5eaa8..3934ba82 100644
--- a/i18n/english.js
+++ b/i18n/english.js
@@ -315,13 +315,15 @@ const ui = {
author: "name or email",
ext: "file extension",
builtin: "node.js module",
- size: "e.g. >50kb"
+ size: "e.g. >50kb",
+ highlighted: "all"
}
},
legend: {
default: "The package is fine.",
warn: "The package has warnings.",
- friendly: "The package is maintained by the same authors as the root package."
+ friendly: "The package is maintained by the same authors as the root package.",
+ highlighted: "The package is part of highlighted packages"
},
lockedNavigation: {
next: "Next",
diff --git a/i18n/french.js b/i18n/french.js
index 95cf6d33..73cf4a80 100644
--- a/i18n/french.js
+++ b/i18n/french.js
@@ -315,13 +315,15 @@ const ui = {
author: "nom ou email",
ext: "extension de fichier",
builtin: "module node.js",
- size: "ex. >50kb"
+ size: "ex. >50kb",
+ highlighted: "all"
}
},
legend: {
default: "Rien à signaler.",
warn: "La dépendance contient des menaces.",
- friendly: "La dépendance est maintenu par des auteurs du package principal."
+ friendly: "La dépendance est maintenu par des auteurs du package principal.",
+ highlighted: "Le package fait partie des packages mis en évidence"
},
lockedNavigation: {
next: "Suivant",
diff --git a/i18n/turkish.js b/i18n/turkish.js
index 4365a215..52ccd603 100644
--- a/i18n/turkish.js
+++ b/i18n/turkish.js
@@ -245,7 +245,8 @@ const ui = {
legend: {
default: "Paket sorunsuz.",
warn: "Pakette uyarılar var.",
- friendly: "Paket, kök paketin yazarlarıyla aynı kişiler tarafından bakılmaktadır."
+ friendly: "Paket, kök paketin yazarlarıyla aynı kişiler tarafından bakılmaktadır.",
+ highlighted: "Paket, vurgulanan paketlerin bir parçasıdır"
},
lockedNavigation: {
next: "Sonraki",
diff --git a/public/components/command-palette/command-palette.js b/public/components/command-palette/command-palette.js
index 0c9a9d70..a99f899c 100644
--- a/public/components/command-palette/command-palette.js
+++ b/public/components/command-palette/command-palette.js
@@ -11,6 +11,7 @@ import {
FILTER_HAS_HELPERS,
FILTER_MULTI_SELECT,
PRESETS,
+ FILTER_INSTANT_CONFIRM,
computeMatches,
getHelperValues
} from "./filters.js";
@@ -94,12 +95,13 @@ class CommandPalette extends LitElement {
#init = ({ detail: { linker, packages, network } }) => {
this.#linker = linker;
this.#network = network;
- this.#packages = packages.map(({ id, name, version, flags }) => {
+ this.#packages = packages.map(({ id, name, version, flags, isHighlighted }) => {
return {
id: String(id),
name,
version,
- flags
+ flags,
+ isHighlighted
};
});
};
@@ -323,10 +325,15 @@ class CommandPalette extends LitElement {
#selectHelper(helper) {
if (helper.type === "filter") {
- this.inputValue = `${helper.value}:`;
- this.activeFilter = helper.value;
- this.selectedIndex = -1;
- this.results = [];
+ if (FILTER_INSTANT_CONFIRM.has(helper.value)) {
+ this.#addQuery(helper.value, "all");
+ }
+ else {
+ this.inputValue = `${helper.value}:`;
+ this.activeFilter = helper.value;
+ this.selectedIndex = -1;
+ this.results = [];
+ }
}
else {
this.#addQuery(this.activeFilter, helper.value);
diff --git a/public/components/command-palette/filters.js b/public/components/command-palette/filters.js
index 36e77e20..ff3b7618 100644
--- a/public/components/command-palette/filters.js
+++ b/public/components/command-palette/filters.js
@@ -20,7 +20,7 @@ export const VERSION_PRESETS = [
{ label: "≥ 1.0", value: ">=1.0.0" },
{ label: "< 1.0", value: "<1.0.0" }
];
-export const FILTERS_NAME = new Set(["package", "version", "flag", "license", "author", "ext", "builtin", "size"]);
+export const FILTERS_NAME = new Set(["package", "version", "flag", "license", "author", "ext", "builtin", "size", "highlighted"]);
export const PRESETS = [
{ id: "has_vulnerabilities", filter: "flag", value: "hasVulnerabilities" },
{ id: "has_scripts", filter: "flag", value: "hasScript" },
@@ -32,6 +32,8 @@ export const PRESETS = [
export const FILTER_HAS_HELPERS = new Set(["license", "ext", "builtin", "author"]);
// Filters where the mode persists after selection (multi-select)
export const FILTER_MULTI_SELECT = new Set(["flag"]);
+// Filters that auto-confirm immediately on selection (no text input needed)
+export const FILTER_INSTANT_CONFIRM = new Set(["highlighted"]);
/**
* Returns per-flag package counts across the full linker.
@@ -243,6 +245,8 @@ function matchesFilter(opt, filterName, inputValue) {
}
case "flag":
return opt.flags.includes(inputValue);
+ case "highlighted":
+ return inputValue === "all" ? opt.isHighlighted === true : opt.isHighlighted !== true;
default:
return false;
}
diff --git a/public/components/legend/legend.js b/public/components/legend/legend.js
index 46cb9b1d..0a4d28f9 100644
--- a/public/components/legend/legend.js
+++ b/public/components/legend/legend.js
@@ -90,6 +90,7 @@ class Legend extends LitElement {
${this.#createLegendBoxElement(colors.WARN, legend.warn)}
${this.#createLegendBoxElement(colors.FRIENDLY, legend.friendly)}
${this.#createLegendBoxElement(colors.DEFAULT, legend.default)}
+ ${this.#createLegendBoxElement(colors.HIGHLIGHTED, legend.highlighted)}
`;
}
@@ -98,7 +99,7 @@ class Legend extends LitElement {
theme,
text
) {
- const style = `background-color: ${theme.color}; color: ${theme.font.color};`;
+ const style = `background-color: ${theme.color}; color: ${(theme.font ?? COLORS.LIGHT.DEFAULT.font).color};`;
return html`
diff --git a/public/components/views/home/maintainers/maintainers.js b/public/components/views/home/maintainers/maintainers.js
index 0c143b7d..59e261d9 100644
--- a/public/components/views/home/maintainers/maintainers.js
+++ b/public/components/views/home/maintainers/maintainers.js
@@ -238,7 +238,7 @@ width: 16px;
const { packages, email, url = null, npmAvatar } = data;
const personClasses = {
person: true,
- highlighted: this.secureDataSet.isHighlighted(data)
+ highlighted: this.secureDataSet.isHighlightedContact(data)
};
return html`
@@ -289,9 +289,9 @@ width: 16px;
#highlightContacts(authors) {
const highlightedAuthors = authors
- .filter(([_, contact]) => this.secureDataSet.isHighlighted(contact));
+ .filter(([_, contact]) => this.secureDataSet.isHighlightedContact(contact));
- const authorsRest = authors.filter(([_, contact]) => !this.secureDataSet.isHighlighted(contact));
+ const authorsRest = authors.filter(([_, contact]) => !this.secureDataSet.isHighlightedContact(contact));
return [...highlightedAuthors, ...authorsRest];
}
diff --git a/public/main.js b/public/main.js
index 88392420..604cf8fa 100644
--- a/public/main.js
+++ b/public/main.js
@@ -488,12 +488,7 @@ function onSettingsSaved(defaultConfig = null) {
window.settings.config.theme = theme;
window.settings.config.disableExternalRequests = config.disableExternalRequests;
- if (theme === "dark") {
- document.body.classList.add("dark");
- }
- else {
- document.body.classList.remove("dark");
- }
+ document.body.classList.toggle("dark", theme === "dark");
await secureDataSet.init(
secureDataSet.data,
diff --git a/test/e2e/command-palette.spec.js b/test/e2e/command-palette.spec.js
index 76e0a4b1..2d56d694 100644
--- a/test/e2e/command-palette.spec.js
+++ b/test/e2e/command-palette.spec.js
@@ -15,7 +15,7 @@ test.describe("[command-palette] presets and actions", () => {
return window.i18n[activeLang].search_command;
});
- await page.locator("body").click();
+ await page.locator(`[data-menu="network--view"].active`).click();
await page.keyboard.press("Control+k");
await expect(page.locator(".backdrop")).toBeVisible();
@@ -105,7 +105,7 @@ test.describe("[command-palette] ignore flags and warnings", () => {
return window.i18n[activeLang].search_command;
});
- await page.locator("body").click();
+ await page.locator(`[data-menu="network--view"].active`).click();
await page.keyboard.press("Control+k");
await expect(page.locator(".backdrop")).toBeVisible();
diff --git a/test/ui/command-palette-filters.test.js b/test/ui/command-palette-filters.test.js
index d8c623ed..9a5d6766 100644
--- a/test/ui/command-palette-filters.test.js
+++ b/test/ui/command-palette-filters.test.js
@@ -18,7 +18,8 @@ const kLinker = new Map([
uniqueLicenseIds: ["MIT"],
composition: { extensions: [".js", ".ts"], required_nodejs: ["fs", "path"] },
author: { name: "TJ Holowaychuk" },
- size: 102_400
+ size: 102_400,
+ isHighlighted: true
}],
[1, {
name: "lodash",
@@ -27,7 +28,8 @@ const kLinker = new Map([
uniqueLicenseIds: ["MIT", "ISC"],
composition: { extensions: [".js", ""], required_nodejs: ["path"] },
author: "John-David Dalton",
- size: 5_000
+ size: 5_000,
+ isHighlighted: true
}],
[2, {
name: "semver",
@@ -232,6 +234,20 @@ describe("computeMatches", () => {
assert.deepEqual(result, new Set());
});
});
+
+ describe("filter: highlighted", () => {
+ it("should match only highlighted packages when value is 'all'", () => {
+ const result = computeMatches(kLinker, "highlighted", "all");
+
+ assert.deepEqual(result, new Set(["0", "1"]));
+ });
+
+ it("should match non-highlighted packages when value is not 'all'", () => {
+ const result = computeMatches(kLinker, "highlighted", "none");
+
+ assert.deepEqual(result, new Set(["2"]));
+ });
+ });
});
describe("getFlagCounts", () => {
diff --git a/workspaces/vis-network/src/constants.ts b/workspaces/vis-network/src/constants.ts
index 23560ca3..1052237d 100644
--- a/workspaces/vis-network/src/constants.ts
+++ b/workspaces/vis-network/src/constants.ts
@@ -9,6 +9,7 @@
export type Color = {
color: string;
+ border?: string;
font: {
color: string;
background?: string;
@@ -18,9 +19,9 @@ export type Color = {
export const COLORS = Object.freeze({
LIGHT: {
SELECTED: {
- color: "#4527A0",
+ color: "#BFC5E0",
font: {
- color: "#FFF"
+ color: "#443730"
}
},
SELECTED_GROUP: {
@@ -36,23 +37,29 @@ export const COLORS = Object.freeze({
}
},
DEFAULT: {
- color: "#E3F2FD",
+ color: "#BEE7E8",
font: {
color: "#121533"
}
},
WARN: {
- color: "#EF5350",
+ color: "#FFBFA0",
font: {
- color: "#FFF"
+ color: "#6B2737"
}
},
FRIENDLY: {
- color: "#e3fde3",
+ color: "#EDEEC0",
font: {
color: "#0e4522"
}
},
+ HIGHLIGHTED: {
+ border: "#EA9010",
+ color: "",
+ font: { color: "" },
+ margin: 12
+ },
CONNECTED_IN: {
color: "#C8E6C9",
font: {
@@ -109,6 +116,11 @@ export const COLORS = Object.freeze({
color: "#FFF"
}
},
+ HIGHLIGHTED: {
+ border: "#dec42c",
+ color: "",
+ font: { color: "" }
+ },
CONNECTED_IN: {
color: "rgb(89, 44, 109)",
font: {
diff --git a/workspaces/vis-network/src/dataset.ts b/workspaces/vis-network/src/dataset.ts
index 3c22c74e..52895ce4 100644
--- a/workspaces/vis-network/src/dataset.ts
+++ b/workspaces/vis-network/src/dataset.ts
@@ -13,6 +13,7 @@ import type {
// Import Internal Dependencies
import * as utils from "./utils.ts";
+import type { VisEdge, VisNode } from "./network.ts";
declare global {
interface Window {
@@ -43,6 +44,7 @@ export type LinkerEntry = DependencyVersion & {
hidden: boolean;
hasWarnings: boolean;
isFriendly: boolean;
+ isHighlighted: boolean;
};
export interface PackageInfo {
@@ -53,36 +55,16 @@ export interface PackageInfo {
flags: string;
links: DependencyLinks | undefined;
isFriendly: boolean;
+ isHighlighted: boolean;
}
export type AuthorInfo = Maintainer & {
packages: Set;
};
-export interface VisNode {
- id: number;
- label: string;
- color: string;
- font: {
- color: string;
- background?: string;
- multi: string;
- };
- hidden?: boolean;
-}
-
-export interface VisEdge {
- id?: string | number;
- from: number;
- to: number;
- label?: string;
- font?: {
- background: string;
- };
-}
-
export default class NodeSecureDataSet extends EventTarget {
#highligthedContacts!: HighlightedContacts;
+ #highlightedPackages;
flagsToIgnore: Set;
warningsToIgnore: Set;
@@ -183,6 +165,8 @@ export default class NodeSecureDataSet extends EventTarget {
return acc;
}, { names: new Set(), emails: new Set() });
+ this.#highlightedPackages = new Set(data.highlighted.packages);
+
const dependencies = Object.entries(data.dependencies);
this.dependenciesCount = dependencies.length;
@@ -244,6 +228,8 @@ export default class NodeSecureDataSet extends EventTarget {
})
);
+ const isHighlighted = this.#isHighlightedPackage(packageName, currVersion);
+
this.packages.push({
id,
name: packageName,
@@ -251,7 +237,8 @@ export default class NodeSecureDataSet extends EventTarget {
hasWarnings,
flags: flagStr.replace(/\s/g, ""),
links,
- isFriendly
+ isFriendly,
+ isHighlighted
});
const label = `${packageName}@${currVersion}${flagStr}\n[${prettyBytes(size)}]`;
@@ -259,6 +246,7 @@ export default class NodeSecureDataSet extends EventTarget {
id,
hasWarnings,
isFriendly,
+ isHighlighted,
theme: this.theme.toUpperCase()
});
@@ -268,14 +256,17 @@ export default class NodeSecureDataSet extends EventTarget {
version: currVersion,
hidden: false,
hasWarnings,
- isFriendly
+ isFriendly,
+ isHighlighted
};
this.linker.set(Number(id), linkerEntry);
+ const { color: nodeColor, font: nodeFont, ...colorRest } = color;
this.rawNodesData.push({
id,
label,
- color: color.color,
- font: { ...color.font, multi: "html" }
+ color: nodeColor,
+ font: { ...nodeFont, multi: "html" },
+ ...colorRest
});
for (const [depName, depVersion] of Object.entries(usedBy)) {
@@ -339,13 +330,17 @@ export default class NodeSecureDataSet extends EventTarget {
return { nodes, edges };
}
- isHighlighted(
+ isHighlightedContact(
contact: { name?: string; email?: string; }
): boolean {
return this.#highligthedContacts.names.has(contact.name ?? "") ||
this.#highligthedContacts.emails.has(contact.email ?? "");
}
+ #isHighlightedPackage(name: string, version: string): boolean {
+ return this.#highlightedPackages.has(name) || this.#highlightedPackages.has(`${name}@${version}`);
+ }
+
findPackagesByName(
name: string
): PackageInfo[] {
diff --git a/workspaces/vis-network/src/index.ts b/workspaces/vis-network/src/index.ts
index a616a375..3a69c5fd 100644
--- a/workspaces/vis-network/src/index.ts
+++ b/workspaces/vis-network/src/index.ts
@@ -12,11 +12,14 @@ export type {
LinkerEntry,
PackageInfo,
AuthorInfo,
- VisNode,
- VisEdge,
Contributor
} from "./dataset.ts";
+export type {
+ VisNode,
+ VisEdge
+} from "./network.ts";
+
export {
getJSON,
getFlagsEmojisInlined,
diff --git a/workspaces/vis-network/src/network.ts b/workspaces/vis-network/src/network.ts
index d18cf226..525109dd 100644
--- a/workspaces/vis-network/src/network.ts
+++ b/workspaces/vis-network/src/network.ts
@@ -8,9 +8,7 @@ import * as CONSTANTS from "./constants.ts";
import * as utils from "./utils.ts";
import type NodeSecureDataSet from "./dataset.ts";
import type {
- LinkerEntry,
- VisNode,
- VisEdge
+ LinkerEntry
} from "./dataset.ts";
import type { I18n } from "./constants.ts";
@@ -25,7 +23,7 @@ export const NETWORK_OPTIONS = {
vadjust: 2,
size: 40
},
- margin: 20,
+ margin: 35,
shadow: {
enabled: true,
color: "rgba(20, 20, 20, 0.1)"
@@ -81,6 +79,37 @@ interface NetworkClickParams {
};
}
+interface VisAdvancedColor {
+ border?: string;
+ background?: string;
+}
+
+export interface VisNode {
+ id: number;
+ label: string;
+ color: string | VisAdvancedColor;
+ border?: string;
+ borderWidth?: number;
+ font: {
+ color: string;
+ background?: string;
+ multi: string;
+ };
+ hidden?: boolean;
+ shadow?: { enabled: boolean; };
+ shapeProperties?: { borderDashes: number[] | boolean; };
+}
+
+export interface VisEdge {
+ id?: string | number;
+ from: number;
+ to: number;
+ label?: string;
+ font?: {
+ background: string;
+ };
+}
+
type ColorPalette = (typeof CONSTANTS.COLORS)[keyof typeof CONSTANTS.COLORS];
export default class NodeSecureNetwork {
@@ -263,6 +292,12 @@ export default class NodeSecureNetwork {
this.colors.HARDTOREAD;
Object.assign(node, color);
+
+ const entry = this.linker.get(Number(node.id));
+ if (entry?.isHighlighted) {
+ node.borderWidth = 1;
+ node.shapeProperties = { borderDashes: false };
+ }
}
for (const nodeId of nodeIdsToHighlight) {
@@ -310,9 +345,9 @@ export default class NodeSecureNetwork {
this.highlightEnabled = false;
for (const node of allNodes) {
- const { id, hasWarnings, isFriendly } = this.linker.get(Number(node.id))!;
+ const { id, hasWarnings, isFriendly, isHighlighted } = this.linker.get(Number(node.id))!;
- Object.assign(node, utils.getNodeColor({ id, hasWarnings, theme: this.theme, isFriendly }));
+ Object.assign(node, utils.getNodeColor({ id, hasWarnings, theme: this.theme, isFriendly, isHighlighted }));
}
this.lastHighlightedIds = null;
@@ -363,6 +398,34 @@ export default class NodeSecureNetwork {
}
}
+ /**
+ * Returns the selected color for a node, preserving highlighted border+shadow if applicable.
+ * @param {number} nodeId
+ * @param {"SELECTED" | "SELECTED_LOCK"} colorKey
+ */
+ #selectedColor(nodeId: number, colorKey: "SELECTED" | "SELECTED_LOCK") {
+ const entry = this.linker.get(Number(nodeId));
+ const base = this.colors[colorKey];
+
+ if (!entry?.isHighlighted) {
+ return base;
+ }
+
+ const borderColor = CONSTANTS.COLORS[this.theme].HIGHLIGHTED.border;
+
+ return {
+ color: {
+ background: base.color,
+ border: borderColor
+ },
+ font: base.font,
+ borderWidth: 3,
+ shapeProperties: {
+ borderDashes: [6, 3]
+ }
+ };
+ }
+
lockedNeighbourHighlight(
params: NetworkClickParams | undefined
): boolean {
@@ -386,7 +449,7 @@ export default class NodeSecureNetwork {
}
const color = node.id === selectedNode ?
- this.colors.SELECTED_LOCK :
+ this.#selectedColor(node.id, "SELECTED_LOCK") :
this.colors.SELECTED_GROUP;
Object.assign(node, color);
@@ -448,6 +511,13 @@ export default class NodeSecureNetwork {
// mark all nodes as hard to read.
for (const node of Object.values(allNodes)) {
Object.assign(node, this.colors.HARDTOREAD);
+
+ const entry = this.linker.get(Number(node.id));
+
+ if (entry?.isHighlighted) {
+ node.borderWidth = 1;
+ node.shapeProperties = { borderDashes: false };
+ }
}
// get the second degree nodes
@@ -471,7 +541,7 @@ export default class NodeSecureNetwork {
}
// the main node gets its own color and its label back.
- Object.assign(allNodes[selectedNode], this.colors.SELECTED);
+ Object.assign(allNodes[selectedNode], this.#selectedColor(selectedNode, "SELECTED"));
// select and label edges connected to the selected node
const connectedEdges = this.network.getConnectedEdges(selectedNode);
@@ -496,9 +566,9 @@ export default class NodeSecureNetwork {
else if (this.highlightEnabled) {
this.highlightEnabled = false;
for (const node of Object.values(allNodes)) {
- const { id, hasWarnings, isFriendly } = this.linker.get(Number(node.id))!;
+ const { id, hasWarnings, isFriendly, isHighlighted } = this.linker.get(Number(node.id))!;
- Object.assign(node, utils.getNodeColor({ id, hasWarnings, theme: this.theme, isFriendly }));
+ Object.assign(node, utils.getNodeColor({ id, hasWarnings, theme: this.theme, isFriendly, isHighlighted }));
}
}
diff --git a/workspaces/vis-network/src/types.ts b/workspaces/vis-network/src/types.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/workspaces/vis-network/src/utils.ts b/workspaces/vis-network/src/utils.ts
index 2397599c..0f224b33 100644
--- a/workspaces/vis-network/src/utils.ts
+++ b/workspaces/vis-network/src/utils.ts
@@ -2,7 +2,7 @@
import { getManifestEmoji } from "@nodesecure/flags/web";
// Import Internal Dependencies
-import * as CONSTANTS from "./constants.ts";
+import { COLORS, type Color } from "./constants.ts";
declare global {
interface Window {
@@ -47,6 +47,7 @@ export interface NodeColorOptions {
hasWarnings?: boolean;
theme?: string;
isFriendly?: boolean;
+ isHighlighted?: boolean;
}
export function getNodeColor(
@@ -56,23 +57,44 @@ export function getNodeColor(
id,
hasWarnings = false,
theme = "LIGHT",
- isFriendly = false
+ isFriendly = false,
+ isHighlighted = false
} = options;
- const palette = CONSTANTS.COLORS[theme as keyof typeof CONSTANTS.COLORS];
+ const palette = COLORS[theme as keyof typeof COLORS];
+ let nodeColor: Color | undefined;
// id 0 is the root package (so by default he is highlighted as selected).
if (id === 0) {
return palette.SELECTED;
}
else if (hasWarnings) {
- return palette.WARN;
+ nodeColor = palette.WARN;
}
else if (isFriendly) {
- return palette.FRIENDLY;
+ nodeColor = palette.FRIENDLY;
+ }
+ else {
+ nodeColor = palette.DEFAULT;
+ }
+
+ if (isHighlighted) {
+ const borderColor = palette.HIGHLIGHTED.border;
+
+ return {
+ color: {
+ background: nodeColor.color,
+ border: borderColor
+ },
+ font: nodeColor.font ?? palette.DEFAULT.font,
+ borderWidth: 3,
+ shapeProperties: {
+ borderDashes: [6, 3]
+ }
+ };
}
- return palette.DEFAULT;
+ return nodeColor;
}
export function getFlagsEmojisInlined(
diff --git a/workspaces/vis-network/test/dataset.test.ts b/workspaces/vis-network/test/dataset.test.ts
index 991dfb27..8c8dbc11 100644
--- a/workspaces/vis-network/test/dataset.test.ts
+++ b/workspaces/vis-network/test/dataset.test.ts
@@ -60,18 +60,50 @@ test("NodeSecureDataSet.prettySize", () => {
test("NodeSecureDataSet.isHighlighted", async() => {
const nsDataSet = new NodeSecureDataSet();
await nsDataSet.init(dataSetPayload);
- assert.equal(nsDataSet.isHighlighted({ name: "Unknown" }), false, "should not be hightlighted");
- assert.equal(nsDataSet.isHighlighted({ name: "Sindre Sorhus" }), true, "name: Sindre Sorhus should be hightlighted");
- assert.equal(nsDataSet.isHighlighted({ name: "Rich Harris" }), true, "name: Rich Harris should be hightlighted");
- assert.equal(nsDataSet.isHighlighted({ email: "rich.harris@gmail.com" }),
+ assert.equal(nsDataSet.isHighlightedContact({ name: "Unknown" }), false, "should not be hightlighted");
+ assert.equal(nsDataSet.isHighlightedContact({ name: "Sindre Sorhus" }), true, "name: Sindre Sorhus should be hightlighted");
+ assert.equal(nsDataSet.isHighlightedContact({ name: "Rich Harris" }), true, "name: Rich Harris should be hightlighted");
+ assert.equal(nsDataSet.isHighlightedContact({ email: "rich.harris@gmail.com" }),
true,
"email: rich.harris@gmail.com should be hightlighted");
- assert.equal(nsDataSet.isHighlighted({ email: "gentilhomme.thomas@gmail.com" }),
+ assert.equal(nsDataSet.isHighlightedContact({ email: "gentilhomme.thomas@gmail.com" }),
true,
"email: gentilhomme.thomas@gmail.com should be hightlighted");
});
+test("NodeSecureDataSet.init should mark highlighted packages by name", async() => {
+ const nsDataSet = new NodeSecureDataSet();
+ await nsDataSet.init(dataSetPayload);
+
+ const pkg3Packages = nsDataSet.findPackagesByName("pkg3");
+ assert.ok(pkg3Packages.length > 0, "should have pkg3 packages");
+ assert.ok(pkg3Packages.every((pkg) => pkg.isHighlighted), "all pkg3 versions should be highlighted (matched by name)");
+});
+
+test("NodeSecureDataSet.init should mark highlighted packages by name@version", async() => {
+ const nsDataSet = new NodeSecureDataSet();
+ await nsDataSet.init(dataSetPayload);
+
+ const pkg2Packages = nsDataSet.findPackagesByName("pkg2");
+ const highlighted = pkg2Packages.find((pkg) => pkg.version === "1.0.4");
+ const notHighlighted = pkg2Packages.find((pkg) => pkg.version === "1.0.3");
+
+ assert.ok(highlighted, "should find pkg2@1.0.4");
+ assert.ok(notHighlighted, "should find pkg2@1.0.3");
+ assert.equal(highlighted.isHighlighted, true, "pkg2@1.0.4 should be highlighted (matched by name@version)");
+ assert.equal(notHighlighted.isHighlighted, false, "pkg2@1.0.3 should not be highlighted");
+});
+
+test("NodeSecureDataSet.init should not highlight packages absent from highlighted list", async() => {
+ const nsDataSet = new NodeSecureDataSet();
+ await nsDataSet.init(dataSetPayload);
+
+ const pkg1Packages = nsDataSet.findPackagesByName("pkg1");
+ assert.ok(pkg1Packages.length > 0, "should have pkg1 packages");
+ assert.ok(pkg1Packages.every((pkg) => !pkg.isHighlighted), "pkg1 should not be highlighted");
+});
+
test("NodeSecureDataSet.computeAuthors", () => {
const nsDataSet = new NodeSecureDataSet();
nsDataSet.computeAuthor({ name: "John Doe" }, "pkg@1.1");
@@ -156,7 +188,8 @@ test("NodeSecureDataSet.findPackagesByName should have packages when name matche
hasWarnings: false,
flags: "",
links: undefined,
- isFriendly: 0
+ isFriendly: 0,
+ isHighlighted: false
},
{
id: undefined,
@@ -165,7 +198,9 @@ test("NodeSecureDataSet.findPackagesByName should have packages when name matche
hasWarnings: false,
flags: "",
links: undefined,
- isFriendly: 0
+ isFriendly: 0,
+ isHighlighted: true
+
}
];
diff --git a/workspaces/vis-network/test/fixtures/dataset-payload.json b/workspaces/vis-network/test/fixtures/dataset-payload.json
index ccd1add6..2601137f 100644
--- a/workspaces/vis-network/test/fixtures/dataset-payload.json
+++ b/workspaces/vis-network/test/fixtures/dataset-payload.json
@@ -34,6 +34,10 @@
"string-width"
]
}
+ ],
+ "packages": [
+ "pkg3",
+ "pkg2@1.0.4"
]
},
"dependencies": {