feat: item color map, fix regex matching, fix sort order, fix resize handle

This commit is contained in:
Sebastian Seedorf
2026-06-04 11:04:58 +02:00
parent b9377daa04
commit 955b0a890d
7 changed files with 562 additions and 41 deletions

View File

@@ -28,12 +28,15 @@ export const GET = withAuth(async (req: NextRequest) => {
if (itemsWhitelist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
// Each pattern is expanded to matching keys (tested against key AND localized name).
// Union all patterns — if a pattern matches nothing, it contributes no keys.
const keys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
if (keys.length === 0) return NextResponse.json([]);
conditions.push(`item_key = ANY($${i++})`);
values.push(keys);
const localeKeys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
const sqlPattern = itemsWhitelist.map(p => `(${p})`).join('|');
const orConds = [`item_key ~* $${i++}`];
values.push(sqlPattern);
if (localeKeys.length > 0) {
orConds.push(`item_key = ANY($${i++})`);
values.push(localeKeys);
}
conditions.push(`(${orConds.join(' OR ')})`);
} else {
conditions.push(`item_key = ANY($${i++})`);
values.push(itemsWhitelist);
@@ -43,12 +46,15 @@ export const GET = withAuth(async (req: NextRequest) => {
if (itemsBlacklist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const keys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
// If blacklist pattern matches nothing, nothing to exclude — skip condition
if (keys.length > 0) {
conditions.push(`item_key != ALL($${i++})`);
values.push(keys);
const localeKeys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
const sqlPattern = itemsBlacklist.map(p => `(${p})`).join('|');
const andConds = [`item_key !~* $${i++}`];
values.push(sqlPattern);
if (localeKeys.length > 0) {
andConds.push(`item_key != ALL($${i++})`);
values.push(localeKeys);
}
conditions.push(`(${andConds.join(' AND ')})`);
} else {
conditions.push(`item_key != ALL($${i++})`);
values.push(itemsBlacklist);
@@ -78,10 +84,15 @@ export const GET = withAuth(async (req: NextRequest) => {
if (itemsWhitelist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const keys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
if (keys.length === 0) return NextResponse.json([]);
baseConditions.push(`item_key = ANY($${j++})`);
baseValues.push(keys);
const localeKeys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
const sqlPattern = itemsWhitelist.map(p => `(${p})`).join('|');
const orConds = [`item_key ~* $${j++}`];
baseValues.push(sqlPattern);
if (localeKeys.length > 0) {
orConds.push(`item_key = ANY($${j++})`);
baseValues.push(localeKeys);
}
baseConditions.push(`(${orConds.join(' OR ')})`);
} else {
baseConditions.push(`item_key = ANY($${j++})`);
baseValues.push(itemsWhitelist);
@@ -90,11 +101,15 @@ export const GET = withAuth(async (req: NextRequest) => {
if (itemsBlacklist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const keys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
if (keys.length > 0) {
baseConditions.push(`item_key != ALL($${j++})`);
baseValues.push(keys);
const localeKeys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
const sqlPattern = itemsBlacklist.map(p => `(${p})`).join('|');
const andConds = [`item_key !~* $${j++}`];
baseValues.push(sqlPattern);
if (localeKeys.length > 0) {
andConds.push(`item_key != ALL($${j++})`);
baseValues.push(localeKeys);
}
baseConditions.push(`(${andConds.join(' AND ')})`);
} else {
baseConditions.push(`item_key != ALL($${j++})`);
baseValues.push(itemsBlacklist);

87
web/bin/fix-colors.ts Normal file
View File

@@ -0,0 +1,87 @@
import { readFileSync, writeFileSync } from 'fs';
const CSV = 'public/factorio_item_colors.csv';
function hexToHsl(h: string): [number, number, number] {
let r = parseInt(h.slice(1, 3), 16) / 255, g = parseInt(h.slice(3, 5), 16) / 255, b = parseInt(h.slice(5, 7), 16) / 255;
const mx = Math.max(r, g, b), mn = Math.min(r, g, b), l = (mx + mn) / 2;
if (mx === mn) return [0, 0, Math.round(l * 100)];
const d = mx - mn, s = l > 0.5 ? d / (2 - mx - mn) : d / (mx + mn);
let hue = 0;
if (mx === r) hue = ((g - b) / d + (g < b ? 6 : 0)) * 60;
else if (mx === g) hue = ((b - r) / d + 2) * 60;
else hue = ((r - g) / d + 4) * 60;
return [Math.round(hue), Math.round(s * 100), Math.round(l * 100)];
}
function hslToHex(h: number, s: number, l: number): string {
s /= 100; l /= 100;
const c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs(((h / 60) % 2) - 1)), m = l - c / 2;
let r = 0, g = 0, b = 0;
if (h < 60) { r = c; g = x; } else if (h < 120) { r = x; g = c; } else if (h < 180) { g = c; b = x; }
else if (h < 240) { g = x; b = c; } else if (h < 300) { r = x; b = c; } else { r = c; b = x; }
const to = (v: number) => Math.round((v + m) * 255).toString(16).padStart(2, '0');
return '#' + to(r) + to(g) + to(b);
}
function djb2(s: string): number {
let h = 5381;
for (let i = 0; i < s.length; i++) h = (h * 33) ^ s.charCodeAt(i);
return h >>> 0;
}
function hueDist(a: number, b: number): number {
const d = Math.abs(a - b);
return Math.min(d, 360 - d);
}
const lines = readFileSync(CSV, 'utf-8').trim().split('\n');
const header = lines[0];
const raw = lines.slice(1).filter(l => l.trim()).map(l => {
const [k, c] = l.split(',');
return { key: k.trim(), color: c.trim() };
});
// Dedup by key
const seen = new Set<string>();
const rows: typeof raw = [];
for (const r of raw) { if (seen.has(r.key)) continue; seen.add(r.key); rows.push(r); }
const n = rows.length;
const parent = Array.from({ length: n }, (_, i) => i);
function find(x: number): number {
while (parent[x] !== x) { parent[x] = parent[parent[x]]; x = parent[x]; }
return x;
}
function union(a: number, b: number) { parent[find(a)] = find(b); }
const hsl = rows.map((r, i) => ({ ...r, hsl: hexToHsl(r.color), idx: i }));
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
if (hueDist(hsl[i].hsl[0], hsl[j].hsl[0]) <= 0.5 &&
Math.abs(hsl[i].hsl[2] - hsl[j].hsl[2]) <= 0.5)
union(i, j);
}
}
const groups = new Map<number, typeof hsl>();
for (let i = 0; i < n; i++) {
const root = find(i);
if (!groups.has(root)) groups.set(root, []);
groups.get(root)!.push(hsl[i]);
}
let fixed = 0;
for (const [, items] of groups) {
if (items.length < 2) continue;
items.sort((a, b) => a.idx - b.idx);
const [oh, os, ol] = items[0].hsl;
for (let i = 1; i < items.length; i++) {
const hash = djb2(items[i].key);
const h = (oh + (hash % 5 - 2) + i * 7 + 360) % 360;
const l = Math.max(0, Math.min(100, ol + ((hash >> 4) % 5 - 2)));
items[i].color = hslToHex(h, os, l);
fixed++;
}
}
hsl.sort((a, b) => a.idx - b.idx);
writeFileSync(CSV, header + '\n' + hsl.map(r => `${r.key},${r.color}`).join('\n') + '\n');
if (fixed) console.log(`Fixed ${fixed} close colors`);

View File

@@ -2,8 +2,11 @@
import 'uplot/dist/uPlot.min.css';
import uPlot from 'uplot';
import { useState, useEffect } from 'react';
import { useApp } from '@/lib/context';
import { resolveName } from '@/lib/localization';
import { getColorMap } from '@/lib/colors';
import type { ColorMap } from '@/lib/colors';
import { CardShell } from './CardShell';
import { makeYScale, makeAnnotationHooks, makeSignalsSeries, makeSignalsAxes, CURSOR_NO_DRAG } from './plotHelpers';
import { buildSeriesData } from './seriesData';
@@ -24,6 +27,8 @@ interface Props {
export default function SignalsChart({ config, rows, sessions, alerts, timeMode, onEdit, onDelete }: Props) {
const { localeMap } = useApp();
const locale = typeof navigator !== 'undefined' ? navigator.language : 'de-DE';
const [colorMap, setColorMap] = useState<ColorMap>(new Map());
useEffect(() => { getColorMap().then(setColorMap); }, []);
const { containerRef, legendRef } = usePlot(
(el, w, h, lRef) => {
@@ -45,7 +50,7 @@ export default function SignalsChart({ config, rows, sessions, alerts, timeMode,
if (lRef.current) lRef.current.appendChild(legendEl);
},
},
series: makeSignalsSeries(keys, timeMode, key => resolveName(key, localeMap)),
series: makeSignalsSeries(keys, timeMode, key => resolveName(key, localeMap), colorMap),
axes: makeSignalsAxes(timeMode, locale),
scales: {
x: { time: false },

View File

@@ -1,6 +1,9 @@
import { useState, useEffect } from 'react';
import { useApp } from '@/lib/context';
import { resolveName } from '@/lib/localization';
import { formatSI } from '@/lib/formatNumber';
import { getColorMap, getItemColor } from '@/lib/colors';
import type { ColorMap } from '@/lib/colors';
import { CardShell } from './CardShell';
import type { ChartConfig, SignalRow } from '@/lib/types';
@@ -13,6 +16,8 @@ interface Props {
export default function TableViz({ config, rows, onEdit, onDelete }: Props) {
const { localeMap } = useApp();
const [colorMap, setColorMap] = useState<ColorMap>(new Map());
useEffect(() => { getColorMap().then(setColorMap); }, []);
const latest = new Map<string, { green?: number; red?: number }>();
for (const row of rows) {
@@ -37,7 +42,11 @@ export default function TableViz({ config, rows, onEdit, onDelete }: Props) {
const [combinator, item_key] = key.split('::');
return (
<tr key={key} className="border-t border-gray-800 hover:bg-gray-800/50">
<td className="px-2 py-0.5">{resolveName(item_key, localeMap)}</td>
<td className="px-2 py-0.5 flex items-center gap-1.5">
<span className="inline-block w-2 h-2 rounded-full shrink-0"
style={{ backgroundColor: getItemColor(item_key, colorMap) }} />
{resolveName(item_key, localeMap)}
</td>
<td className="px-2 py-0.5 text-gray-500">{combinator}</td>
{config.signal_type !== 'red' && (
<td className={`px-2 py-0.5 text-right font-mono ${(vals.green ?? 0) < 0 ? 'text-red-400' : 'text-green-400'}`}>

View File

@@ -1,19 +1,8 @@
import uPlot from 'uplot';
import type { ChartConfig } from '@/lib/types';
import { formatSI } from '@/lib/formatNumber';
// --- Color helpers ---
function djb2(s: string): number {
let h = 5381;
for (let i = 0; i < s.length; i++) h = (h * 33) ^ s.charCodeAt(i);
return h >>> 0;
}
function hslColor(key: string): string {
const hue = djb2(key) % 360;
return `hsl(${hue},70%,60%)`;
}
import type { ColorMap } from '@/lib/colors';
import { getItemColor } from '@/lib/colors';
const SEMANTIC_GREEN = '#4ade80';
const SEMANTIC_RED = '#f87171';
@@ -28,6 +17,7 @@ export function getSeriesStyle(
uCombs: number,
uItems: number,
uSigs: number,
colorMap: ColorMap = new Map(),
): SeriesStyle {
const [combinator, item_key, sig] = key.split('::');
@@ -35,9 +25,9 @@ export function getSeriesStyle(
return { color: sig === 'green' ? SEMANTIC_GREEN : SEMANTIC_RED, dash: undefined };
}
if (uItems > 1) {
return { color: hslColor(item_key), dash: uSigs > 1 && sig === 'red' ? [4, 2] : undefined };
return { color: getItemColor(item_key, colorMap), dash: uSigs > 1 && sig === 'red' ? [4, 2] : undefined };
}
return { color: hslColor(combinator), dash: uSigs > 1 && sig === 'red' ? [4, 2] : undefined };
return { color: getItemColor(combinator, colorMap), dash: uSigs > 1 && sig === 'red' ? [4, 2] : undefined };
}
/**
@@ -137,6 +127,7 @@ export function makeSignalsSeries(
keys: string[],
timeMode: 'real' | 'tick',
resolveName: (key: string) => string,
colorMap: ColorMap = new Map(),
): uPlot.Series[] {
const uCombs = new Set(keys.map(k => k.split('::')[0])).size;
const uItems = new Set(keys.map(k => k.split('::')[1])).size;
@@ -154,7 +145,7 @@ export function makeSignalsSeries(
xSeries,
...keys.map(k => {
const [, item_key] = k.split('::');
const { color, dash } = getSeriesStyle(k, uCombs, uItems, uSigs);
const { color, dash } = getSeriesStyle(k, uCombs, uItems, uSigs, colorMap);
return {
label: getSeriesLabel(k, uCombs, uItems, uSigs, resolveName(item_key)),
stroke: color,

35
web/lib/colors.ts Normal file
View File

@@ -0,0 +1,35 @@
export type ColorMap = Map<string, string>;
declare global { var __colorMapCache: ColorMap | undefined; }
export function parseColorCsv(text: string): ColorMap {
const map: ColorMap = new Map();
const lines = text.split(/\r?\n/).slice(1);
for (const line of lines) {
if (!line.trim()) continue;
const [key, color] = line.split(',');
if (key && color) map.set(key.trim(), color.trim());
}
return map;
}
export async function getColorMap(): Promise<ColorMap> {
if (globalThis.__colorMapCache) return globalThis.__colorMapCache;
try {
const res = await fetch('/factorio_item_colors.csv');
globalThis.__colorMapCache = res.ok ? parseColorCsv(await res.text()) : new Map();
} catch {
globalThis.__colorMapCache = new Map();
}
return globalThis.__colorMapCache;
}
function djb2(s: string): number {
let h = 5381;
for (let i = 0; i < s.length; i++) h = (h * 33) ^ s.charCodeAt(i);
return h >>> 0;
}
export function getItemColor(key: string, colorMap: ColorMap): string {
return colorMap.get(key) ?? `hsl(${djb2(key) % 360},70%,60%)`;
}

View File

@@ -0,0 +1,379 @@
item_key,color
copper-ore,#bf8040
copper-plate,#b87333
copper-cable,#e65100
iron-ore,#8d6e63
iron-plate,#bdbdbd
iron-gear-wheel,#a0a0a0
iron-stick,#888888
steel-plate,#757575
steel-gear-wheel,#5a5a5a
steel-beam,#424242
stone,#a1887f
stone-brick,#b8956a
coal,#37474f
uranium-ore,#66bb6a
uranium-238,#7cb342
uranium-235,#64dd17
crude-oil,#1a1a1a
heavy-oil,#4e342e
light-oil,#ff8a65
petroleum-gas,#1a0030
lubricant,#1b5e20
sulfuric-acid,#b2dfdb
water,#1565c0
steam,#e1f5fe
wood,#6d4c41
raw-wood,#3e2723
plastic-bar,#b0bec5
sulfur,#fff176
explosives,#ef5350
battery,#ff9800
empty-barrel,#90a4ae
filled-barrel,#546e7a
electronic-circuit,#4caf50
advanced-circuit,#e53935
processing-unit,#1565d2
automation-science-pack,#d32f2f
logistic-science-pack,#43a047
military-science-pack,#616161
chemical-science-pack,#00acc1
production-science-pack,#ff8f00
utility-science-pack,#7b1fa2
space-science-pack,#f48fb1
pipe,#78909c
engine-unit,#795548
electric-engine-unit,#4fc3f7
flying-robot-frame,#5e35b1
rocket-fuel,#ff5722
rocket-control-unit,#2e7d32
low-density-structure,#bcaaa4
heat-pipe,#bf360c
heat-exchanger,#263238
steam-turbine,#455a64
concrete,#9e9e9e
refined-concrete,#6d6d6d
landfill,#388e3c
cliff-explosives,#d84315
nuclear-fuel,#00c853
solid-fuel,#0d47a1
grenade,#5d4037
cluster-grenade,#c62828
landmine,#4a148c
fish,#29b6f6
glass,#4dd0e1
rail,#9e9e9e
rail-signal,#4caf50
rail-chain-signal,#e53935
train-stop,#37474f
locomotive,#d32f2f
cargo-wagon,#8d6e63
fluid-wagon,#1565c0
artillery-wagon,#424242
artillery-turret,#424242
flamethrower-turret,#d84315
gun-turret,#616161
laser-turret,#e53935
radar,#388e3c
roboport,#2196f3
construction-robot,#4caf50
logistic-robot,#1565c0
speed-module,#f44336
speed-module-2,#e91e63
speed-module-3,#9c27b0
speed-module-5,#4a148c
effectivity-module,#4caf50
effectivity-module-2,#388e3c
effectivity-module-3,#2e7d32
effectivity-module-4,#1b5e20
productivity-module,#ff9800
productivity-module-2,#ef6c00
productivity-module-3,#e65100
productivity-module-5,#bf360c
beacon,#2196f3
substation,#9c27b0
medium-electric-pole,#78909c
big-electric-pole,#546e7a
small-electric-pole,#90a4ae
small-iron-electric-pole,#90a4ae
steel-chest,#757575
iron-chest,#8d6e63
wooden-chest,#6d4c41
transport-belt,#bdbdbd
fast-transport-belt,#4fc3f7
express-transport-belt,#e53935
underground-belt,#bdbdbd
fast-underground-belt,#4fc3f7
express-underground-belt,#e53935
splitter,#bdbdbd
fast-splitter,#4fc3f7
express-splitter,#e53935
long-handed-inserter,#795548
fast-inserter,#795548
burner-inserter,#795548
inserter,#795548
stack-inserter,#4e342e
burner-mining-drill,#795548
electric-mining-drill,#795548
area-mining-drill,#795548
electric-furnace,#455a64
industrial-furnace,#455a64
assembling-machine-3,#616161
centrifuge,#546e7a
chemical-plant,#00acc1
oil-refinery,#1a1a1a
pumpjack,#4e342e
offshore-pump,#1565c0
boiler,#9e9e9e
steam-engine,#78909c
solar-panel,#ffb74d
accumulator,#2196f3
lamp,#ffeb3b
constant-combinator,#546e7a
decider-combinator,#546e7a
arithmetic-combinator,#546e7a
power-switch,#ff8f00
programmable-speaker,#7b1fa2
aai-signal-receiver,#37474f
aai-signal-transmitter,#37474f
textplate-small-copper,#e65100
used-up-uranium-fuel-cell,#37474f
uranium-fuel-cell,#66bb6a
electric-motor,#4fc3f7
motor,#4fc3f7
automation-core,#1565c0
iron-beam,#888888
kr-advanced-solar-panel,#ffb74d
kr-advanced-transport-belt,#26c6da
kr-advanced-loader,#00acc1
kr-advanced-splitter,#00bcd4
kr-superior-inserter,#795548
kr-superior-filter-inserter,#5d4037
kr-superior-long-inserter,#795548
kr-superior-long-filter-inserter,#5d4037
kr-superior-underground-belt,#00acc1
kr-superior-loader,#00acc1
kr-fuel-refinery,#bf360c
kr-quarry-drill,#795548
kr-express-loader,#00838f
kr-electric-mining-drill-mk2,#4e342e
kr-steel-pipe,#757575
kr-steel-pipe-to-ground,#757575
kr-fluid-storage-2,#1565c0
kr-se-loader,#00acc1
beryllium,#81c784
beryllium-ore,#558b2f
beryllium-sulfate,#00695c
holmium,#ba68c8
holmium-ore,#ab47bc
holmium-solution,#e1bee7
cryonite,#00bcd4
cryonite-rod,#26c6da
cryonite-solution,#80deea
iridium,#cfd8dc
iridium-ore,#689f38
iridium-ingot,#e0e0e0
vulcanite,#ff7043
vulcanite-block,#e64a19
vulcanite-powder,#ffab91
imersite,#8e24aa
imersite-crystal,#283593
imersite-powder,#ce93d8
vita,#8bc34a
vita-extract,#1b5e20
vita-germination,#006064
naquium,#ffd54f
naquium-ore,#ffca28
naquium-ingot,#fff9c4
methane-ice,#b2ebf2
core-fragment,#4db6ac
rare-metals,#b0b0b0
raw-rare-metals,#b0b0b0
raw-imersite,#8e24aa
sand,#c2b280
silicon,#78909c
quartz,#e0e0e0
coke,#424242
se-copper-ingot,#b87333
se-iron-ingot,#8d6e63
se-steel-ingot,#757575
se-beryllium-ingot,#81c784
se-holmium-ingot,#ba68c8
se-iridium-ingot,#cfd8dc
se-cryonite-rod,#26c6da
se-cryonite-slush,#80deea
se-vulcanite-crushed,#ff7043
se-vulcanite-enriched,#ff5722
se-vulcanite-block,#e64a19
imersium-plate,#7b1fa2
se-kr-imersium-sulfide,#7b1fa2
se-kr-fine-imersite-powder,#ce93d8
enriched-iron,#8d6e63
electronic-components,#4caf50
empty-data-card,#512da8
heat-shielding,#00bfa5
thermodynamic-boiler,#311b92
cryogenic-plant,#0097a7
core-drill,#3d5afe
core-miner,#1a237e
se-quantum-processor,#1565d2
se-holmium-cable,#ba68c8
se-holmium-solenoid,#ba68c8
se-superconductive-cable,#1a237e
se-data-storage-substrate,#4527a0
se-machine-learning-data,#3949ab
se-empty-data,#5c6bc0
se-broken-data,#7986cb
se-junk-data,#9fa8da
se-scrap,#757575
se-contaminated-scrap,#4e342e
se-genetic-data,#66bb6a
se-significant-data,#43a047
se-experimental-genetic-data,#2e7d32
se-atomic-data,#00bcd4
se-star-probe-data,#ffd54f
se-significant-specimen,#00bfa5
se-specimen,#26a69a
se-bio-sludge,#2e7d32
se-nutrient-gel,#00695c
se-nutrient-gel-barrel,#004d40
mineral-water,#4fc3f7
chlorine,#b2dfdb
nitric-acid,#ffcc80
se-bioscrubber,#1b5e20
se-space-coolant-hot,#d84315
se-space-water,#4fc3f7
se-chemical-gel,#8e24aa
se-vitalic-acid,#8bc34a
se-vitalic-reagent,#7cb342
se-vitalic-epoxy,#689f38
se-neural-gel,#b39ddb
se-neural-gel-2,#9575cd
se-plasma-stream,#d50000
se-proton-stream,#e040fb
se-ion-stream,#651fff
se-particle-stream,#304ffe
se-vitamelange-extract,#558b2f
se-vitamelange-spice,#689f38
se-water-ice,#e3f2fd
lithium,#b0bec5
lithium-chloride,#b0bec5
lithium-sulfur-battery,#ce93d8
fertilizer,#4e342e
biomass,#33691e
biomethanol,#43a047
se-surface-teleporter,#1a237e
se-observation-frame,#7cb342
se-observation-frame-blank,#8bc34a
se-core-fragment-se-beryllium,#81c784
se-core-fragment-se-cryonite,#00bcd4
se-core-fragment-se-holmium,#ba68c8
se-core-fragment-se-imersite,#8e24aa
se-core-fragment-se-iridium,#cfd8dc
se-core-fragment-se-naquium,#ffca28
se-core-fragment-se-vita,#8bc34a
se-core-fragment-se-vulcanite,#e64a19
se-core-fragment-omni,#4db6ac
se-core-fragment-se-iridium-ore,#689f38
se-core-fragment-se-vitamelange,#8bc34a
se-aeroframe-bulkhead,#4db6ac
se-aeroframe-scaffold,#26a69a
se-aeroframe-pole,#00897b
se-heavy-girder,#78909c
se-heavy-bearing,#607d8b
se-heavy-composite,#546e7a
se-space-pipe,#4db6ac
se-space-transport-belt,#26c6da
se-space-underground-belt,#00acc1
se-space-splitter,#00bcd4
se-space-accumulator,#4fc3f7
se-space-solar-panel,#ffb74d
se-space-solar-panel-2,#ffa726
se-space-elevator-cable,#4527a0
se-space-pipe-to-ground,#4db6ac
se-space-mirror,#e0e0e0
se-space-rail,#78909c
se-space-platform-scaffold,#bdbdbd
se-space-probe-rocket,#ff7043
se-space-capsule,#42a5f5
se-meteor-defence-ammo,#ef5350
se-dynamic-emitter,#1565c0
se-gammaray-detector,#00bfa5
se-pylon-substation,#ffd54f
se-rocket-launch-pad,#757575
se-rocket-landing-pad,#616161
se-cargo-rocket-fuel-tank,#ff5722
se-cargo-rocket-cargo-pod,#ff8a65
se-cargo-rocket-section,#ffab91
se-cargo-rocket-section-packed,#ffcc80
se-lifesupport-canister,#4fc3f7
se-used-lifesupport-canister,#90a4ae
se-canister,#90a4ae
se-magnetic-canister,#ba68c8
se-iridium-ore,#689f38
se-iridium-ore-crushed,#689f38
se-beryllium-ore,#558b2f
se-beryllium-sulfate,#00695c
se-holmium-ore,#ab47bc
se-holmium-ore-crushed,#ab47bc
se-compact-beacon,#1565c0
se-recycling-facility,#4caf50
se-rocket-science-pack,#e0e0e0
lubricant-barrel,#ffc107
heavy-oil-barrel,#4e342e
light-oil-barrel,#ff8a65
petroleum-gas-barrel,#1a0030
se-material-science-pack-1,#ffb74d
se-material-science-pack-2,#ffa726
se-material-science-pack-3,#ff9800
se-material-science-pack-4,#ef6c00
se-material-testing-pack,#f57c00
se-material-insight,#ffe0b2
se-material-catalogue-1,#ffb74d
se-material-catalogue-2,#ffa726
se-material-catalogue-3,#ff9800
se-material-catalogue-4,#ef6c00
se-astronomic-science-pack-1,#5c6bc0
se-astronomic-science-pack-2,#3949ab
se-astronomic-insight,#c5cae9
se-astronomic-catalogue-1,#5c6bc0
se-astronomic-catalogue-2,#3949ab
se-astronomic-catalogue-3,#283593
se-astronomic-catalogue-4,#1a237e
se-biological-science-pack-1,#69f0ae
se-biological-science-pack-2,#00e676
se-biological-science-pack-3,#00c853
se-biological-science-pack-4,#009624
se-biological-insight,#b9f6ca
se-biological-catalogue-1,#69f0ae
se-biological-catalogue-2,#00e676
se-biological-catalogue-3,#00c853
se-biological-catalogue-4,#009624
se-energy-science-pack-1,#f48fb1
se-energy-science-pack-2,#f06292
se-energy-science-pack-3,#ec407a
se-energy-science-pack-4,#d81b60
se-energy-insight,#fce4ec
se-energy-catalogue-1,#f48fb1
se-energy-catalogue-2,#f06292
se-energy-catalogue-3,#ec407a
se-energy-catalogue-4,#d81b60
se-deep-space-science-pack-1,#b39ddb
se-deep-space-science-pack-2,#9575cd
se-deep-space-science-pack-3,#7e57c2
se-deep-space-science-pack-4,#5e35b1
se-kr-matter-science-pack-1,#ffcc80
se-kr-matter-science-pack-2,#ffb74d
se-kr-matter-liberation-data,#ffe0b2
blank-tech-card,#90a4ae
singularity-tech-card,#1a237e
ltn-combinator,#1a237e
ltn-stop,#37474f
ltn-delivery-address,#2e7d32
ltn-provider-stack-threshold,#f44336
ltn-requester-stack-threshold,#2196f3
ltn-provider-threshold,#d32f2f
ltn-requester-threshold,#1565c0
ltn-max-trains,#4a148c
ltn-max-train-length,#4a148c
ltn-locked-slots,#546e7a
1 item_key color
2 copper-ore #bf8040
3 copper-plate #b87333
4 copper-cable #e65100
5 iron-ore #8d6e63
6 iron-plate #bdbdbd
7 iron-gear-wheel #a0a0a0
8 iron-stick #888888
9 steel-plate #757575
10 steel-gear-wheel #5a5a5a
11 steel-beam #424242
12 stone #a1887f
13 stone-brick #b8956a
14 coal #37474f
15 uranium-ore #66bb6a
16 uranium-238 #7cb342
17 uranium-235 #64dd17
18 crude-oil #1a1a1a
19 heavy-oil #4e342e
20 light-oil #ff8a65
21 petroleum-gas #1a0030
22 lubricant #1b5e20
23 sulfuric-acid #b2dfdb
24 water #1565c0
25 steam #e1f5fe
26 wood #6d4c41
27 raw-wood #3e2723
28 plastic-bar #b0bec5
29 sulfur #fff176
30 explosives #ef5350
31 battery #ff9800
32 empty-barrel #90a4ae
33 filled-barrel #546e7a
34 electronic-circuit #4caf50
35 advanced-circuit #e53935
36 processing-unit #1565d2
37 automation-science-pack #d32f2f
38 logistic-science-pack #43a047
39 military-science-pack #616161
40 chemical-science-pack #00acc1
41 production-science-pack #ff8f00
42 utility-science-pack #7b1fa2
43 space-science-pack #f48fb1
44 pipe #78909c
45 engine-unit #795548
46 electric-engine-unit #4fc3f7
47 flying-robot-frame #5e35b1
48 rocket-fuel #ff5722
49 rocket-control-unit #2e7d32
50 low-density-structure #bcaaa4
51 heat-pipe #bf360c
52 heat-exchanger #263238
53 steam-turbine #455a64
54 concrete #9e9e9e
55 refined-concrete #6d6d6d
56 landfill #388e3c
57 cliff-explosives #d84315
58 nuclear-fuel #00c853
59 solid-fuel #0d47a1
60 grenade #5d4037
61 cluster-grenade #c62828
62 landmine #4a148c
63 fish #29b6f6
64 glass #4dd0e1
65 rail #9e9e9e
66 rail-signal #4caf50
67 rail-chain-signal #e53935
68 train-stop #37474f
69 locomotive #d32f2f
70 cargo-wagon #8d6e63
71 fluid-wagon #1565c0
72 artillery-wagon #424242
73 artillery-turret #424242
74 flamethrower-turret #d84315
75 gun-turret #616161
76 laser-turret #e53935
77 radar #388e3c
78 roboport #2196f3
79 construction-robot #4caf50
80 logistic-robot #1565c0
81 speed-module #f44336
82 speed-module-2 #e91e63
83 speed-module-3 #9c27b0
84 speed-module-5 #4a148c
85 effectivity-module #4caf50
86 effectivity-module-2 #388e3c
87 effectivity-module-3 #2e7d32
88 effectivity-module-4 #1b5e20
89 productivity-module #ff9800
90 productivity-module-2 #ef6c00
91 productivity-module-3 #e65100
92 productivity-module-5 #bf360c
93 beacon #2196f3
94 substation #9c27b0
95 medium-electric-pole #78909c
96 big-electric-pole #546e7a
97 small-electric-pole #90a4ae
98 small-iron-electric-pole #90a4ae
99 steel-chest #757575
100 iron-chest #8d6e63
101 wooden-chest #6d4c41
102 transport-belt #bdbdbd
103 fast-transport-belt #4fc3f7
104 express-transport-belt #e53935
105 underground-belt #bdbdbd
106 fast-underground-belt #4fc3f7
107 express-underground-belt #e53935
108 splitter #bdbdbd
109 fast-splitter #4fc3f7
110 express-splitter #e53935
111 long-handed-inserter #795548
112 fast-inserter #795548
113 burner-inserter #795548
114 inserter #795548
115 stack-inserter #4e342e
116 burner-mining-drill #795548
117 electric-mining-drill #795548
118 area-mining-drill #795548
119 electric-furnace #455a64
120 industrial-furnace #455a64
121 assembling-machine-3 #616161
122 centrifuge #546e7a
123 chemical-plant #00acc1
124 oil-refinery #1a1a1a
125 pumpjack #4e342e
126 offshore-pump #1565c0
127 boiler #9e9e9e
128 steam-engine #78909c
129 solar-panel #ffb74d
130 accumulator #2196f3
131 lamp #ffeb3b
132 constant-combinator #546e7a
133 decider-combinator #546e7a
134 arithmetic-combinator #546e7a
135 power-switch #ff8f00
136 programmable-speaker #7b1fa2
137 aai-signal-receiver #37474f
138 aai-signal-transmitter #37474f
139 textplate-small-copper #e65100
140 used-up-uranium-fuel-cell #37474f
141 uranium-fuel-cell #66bb6a
142 electric-motor #4fc3f7
143 motor #4fc3f7
144 automation-core #1565c0
145 iron-beam #888888
146 kr-advanced-solar-panel #ffb74d
147 kr-advanced-transport-belt #26c6da
148 kr-advanced-loader #00acc1
149 kr-advanced-splitter #00bcd4
150 kr-superior-inserter #795548
151 kr-superior-filter-inserter #5d4037
152 kr-superior-long-inserter #795548
153 kr-superior-long-filter-inserter #5d4037
154 kr-superior-underground-belt #00acc1
155 kr-superior-loader #00acc1
156 kr-fuel-refinery #bf360c
157 kr-quarry-drill #795548
158 kr-express-loader #00838f
159 kr-electric-mining-drill-mk2 #4e342e
160 kr-steel-pipe #757575
161 kr-steel-pipe-to-ground #757575
162 kr-fluid-storage-2 #1565c0
163 kr-se-loader #00acc1
164 beryllium #81c784
165 beryllium-ore #558b2f
166 beryllium-sulfate #00695c
167 holmium #ba68c8
168 holmium-ore #ab47bc
169 holmium-solution #e1bee7
170 cryonite #00bcd4
171 cryonite-rod #26c6da
172 cryonite-solution #80deea
173 iridium #cfd8dc
174 iridium-ore #689f38
175 iridium-ingot #e0e0e0
176 vulcanite #ff7043
177 vulcanite-block #e64a19
178 vulcanite-powder #ffab91
179 imersite #8e24aa
180 imersite-crystal #283593
181 imersite-powder #ce93d8
182 vita #8bc34a
183 vita-extract #1b5e20
184 vita-germination #006064
185 naquium #ffd54f
186 naquium-ore #ffca28
187 naquium-ingot #fff9c4
188 methane-ice #b2ebf2
189 core-fragment #4db6ac
190 rare-metals #b0b0b0
191 raw-rare-metals #b0b0b0
192 raw-imersite #8e24aa
193 sand #c2b280
194 silicon #78909c
195 quartz #e0e0e0
196 coke #424242
197 se-copper-ingot #b87333
198 se-iron-ingot #8d6e63
199 se-steel-ingot #757575
200 se-beryllium-ingot #81c784
201 se-holmium-ingot #ba68c8
202 se-iridium-ingot #cfd8dc
203 se-cryonite-rod #26c6da
204 se-cryonite-slush #80deea
205 se-vulcanite-crushed #ff7043
206 se-vulcanite-enriched #ff5722
207 se-vulcanite-block #e64a19
208 imersium-plate #7b1fa2
209 se-kr-imersium-sulfide #7b1fa2
210 se-kr-fine-imersite-powder #ce93d8
211 enriched-iron #8d6e63
212 electronic-components #4caf50
213 empty-data-card #512da8
214 heat-shielding #00bfa5
215 thermodynamic-boiler #311b92
216 cryogenic-plant #0097a7
217 core-drill #3d5afe
218 core-miner #1a237e
219 se-quantum-processor #1565d2
220 se-holmium-cable #ba68c8
221 se-holmium-solenoid #ba68c8
222 se-superconductive-cable #1a237e
223 se-data-storage-substrate #4527a0
224 se-machine-learning-data #3949ab
225 se-empty-data #5c6bc0
226 se-broken-data #7986cb
227 se-junk-data #9fa8da
228 se-scrap #757575
229 se-contaminated-scrap #4e342e
230 se-genetic-data #66bb6a
231 se-significant-data #43a047
232 se-experimental-genetic-data #2e7d32
233 se-atomic-data #00bcd4
234 se-star-probe-data #ffd54f
235 se-significant-specimen #00bfa5
236 se-specimen #26a69a
237 se-bio-sludge #2e7d32
238 se-nutrient-gel #00695c
239 se-nutrient-gel-barrel #004d40
240 mineral-water #4fc3f7
241 chlorine #b2dfdb
242 nitric-acid #ffcc80
243 se-bioscrubber #1b5e20
244 se-space-coolant-hot #d84315
245 se-space-water #4fc3f7
246 se-chemical-gel #8e24aa
247 se-vitalic-acid #8bc34a
248 se-vitalic-reagent #7cb342
249 se-vitalic-epoxy #689f38
250 se-neural-gel #b39ddb
251 se-neural-gel-2 #9575cd
252 se-plasma-stream #d50000
253 se-proton-stream #e040fb
254 se-ion-stream #651fff
255 se-particle-stream #304ffe
256 se-vitamelange-extract #558b2f
257 se-vitamelange-spice #689f38
258 se-water-ice #e3f2fd
259 lithium #b0bec5
260 lithium-chloride #b0bec5
261 lithium-sulfur-battery #ce93d8
262 fertilizer #4e342e
263 biomass #33691e
264 biomethanol #43a047
265 se-surface-teleporter #1a237e
266 se-observation-frame #7cb342
267 se-observation-frame-blank #8bc34a
268 se-core-fragment-se-beryllium #81c784
269 se-core-fragment-se-cryonite #00bcd4
270 se-core-fragment-se-holmium #ba68c8
271 se-core-fragment-se-imersite #8e24aa
272 se-core-fragment-se-iridium #cfd8dc
273 se-core-fragment-se-naquium #ffca28
274 se-core-fragment-se-vita #8bc34a
275 se-core-fragment-se-vulcanite #e64a19
276 se-core-fragment-omni #4db6ac
277 se-core-fragment-se-iridium-ore #689f38
278 se-core-fragment-se-vitamelange #8bc34a
279 se-aeroframe-bulkhead #4db6ac
280 se-aeroframe-scaffold #26a69a
281 se-aeroframe-pole #00897b
282 se-heavy-girder #78909c
283 se-heavy-bearing #607d8b
284 se-heavy-composite #546e7a
285 se-space-pipe #4db6ac
286 se-space-transport-belt #26c6da
287 se-space-underground-belt #00acc1
288 se-space-splitter #00bcd4
289 se-space-accumulator #4fc3f7
290 se-space-solar-panel #ffb74d
291 se-space-solar-panel-2 #ffa726
292 se-space-elevator-cable #4527a0
293 se-space-pipe-to-ground #4db6ac
294 se-space-mirror #e0e0e0
295 se-space-rail #78909c
296 se-space-platform-scaffold #bdbdbd
297 se-space-probe-rocket #ff7043
298 se-space-capsule #42a5f5
299 se-meteor-defence-ammo #ef5350
300 se-dynamic-emitter #1565c0
301 se-gammaray-detector #00bfa5
302 se-pylon-substation #ffd54f
303 se-rocket-launch-pad #757575
304 se-rocket-landing-pad #616161
305 se-cargo-rocket-fuel-tank #ff5722
306 se-cargo-rocket-cargo-pod #ff8a65
307 se-cargo-rocket-section #ffab91
308 se-cargo-rocket-section-packed #ffcc80
309 se-lifesupport-canister #4fc3f7
310 se-used-lifesupport-canister #90a4ae
311 se-canister #90a4ae
312 se-magnetic-canister #ba68c8
313 se-iridium-ore #689f38
314 se-iridium-ore-crushed #689f38
315 se-beryllium-ore #558b2f
316 se-beryllium-sulfate #00695c
317 se-holmium-ore #ab47bc
318 se-holmium-ore-crushed #ab47bc
319 se-compact-beacon #1565c0
320 se-recycling-facility #4caf50
321 se-rocket-science-pack #e0e0e0
322 lubricant-barrel #ffc107
323 heavy-oil-barrel #4e342e
324 light-oil-barrel #ff8a65
325 petroleum-gas-barrel #1a0030
326 se-material-science-pack-1 #ffb74d
327 se-material-science-pack-2 #ffa726
328 se-material-science-pack-3 #ff9800
329 se-material-science-pack-4 #ef6c00
330 se-material-testing-pack #f57c00
331 se-material-insight #ffe0b2
332 se-material-catalogue-1 #ffb74d
333 se-material-catalogue-2 #ffa726
334 se-material-catalogue-3 #ff9800
335 se-material-catalogue-4 #ef6c00
336 se-astronomic-science-pack-1 #5c6bc0
337 se-astronomic-science-pack-2 #3949ab
338 se-astronomic-insight #c5cae9
339 se-astronomic-catalogue-1 #5c6bc0
340 se-astronomic-catalogue-2 #3949ab
341 se-astronomic-catalogue-3 #283593
342 se-astronomic-catalogue-4 #1a237e
343 se-biological-science-pack-1 #69f0ae
344 se-biological-science-pack-2 #00e676
345 se-biological-science-pack-3 #00c853
346 se-biological-science-pack-4 #009624
347 se-biological-insight #b9f6ca
348 se-biological-catalogue-1 #69f0ae
349 se-biological-catalogue-2 #00e676
350 se-biological-catalogue-3 #00c853
351 se-biological-catalogue-4 #009624
352 se-energy-science-pack-1 #f48fb1
353 se-energy-science-pack-2 #f06292
354 se-energy-science-pack-3 #ec407a
355 se-energy-science-pack-4 #d81b60
356 se-energy-insight #fce4ec
357 se-energy-catalogue-1 #f48fb1
358 se-energy-catalogue-2 #f06292
359 se-energy-catalogue-3 #ec407a
360 se-energy-catalogue-4 #d81b60
361 se-deep-space-science-pack-1 #b39ddb
362 se-deep-space-science-pack-2 #9575cd
363 se-deep-space-science-pack-3 #7e57c2
364 se-deep-space-science-pack-4 #5e35b1
365 se-kr-matter-science-pack-1 #ffcc80
366 se-kr-matter-science-pack-2 #ffb74d
367 se-kr-matter-liberation-data #ffe0b2
368 blank-tech-card #90a4ae
369 singularity-tech-card #1a237e
370 ltn-combinator #1a237e
371 ltn-stop #37474f
372 ltn-delivery-address #2e7d32
373 ltn-provider-stack-threshold #f44336
374 ltn-requester-stack-threshold #2196f3
375 ltn-provider-threshold #d32f2f
376 ltn-requester-threshold #1565c0
377 ltn-max-trains #4a148c
378 ltn-max-train-length #4a148c
379 ltn-locked-slots #546e7a