feat: item color map, fix regex matching, fix sort order, fix resize handle
This commit is contained in:
@@ -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
87
web/bin/fix-colors.ts
Normal 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`);
|
||||
@@ -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 },
|
||||
|
||||
@@ -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'}`}>
|
||||
|
||||
@@ -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
35
web/lib/colors.ts
Normal 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%)`;
|
||||
}
|
||||
379
web/public/factorio_item_colors.csv
Normal file
379
web/public/factorio_item_colors.csv
Normal 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
|
||||
|
Reference in New Issue
Block a user