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 (itemsWhitelist.length > 0) {
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
const localeMap = getServerLocaleMap();
|
const localeMap = getServerLocaleMap();
|
||||||
// Each pattern is expanded to matching keys (tested against key AND localized name).
|
const localeKeys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
|
||||||
// Union all patterns — if a pattern matches nothing, it contributes no keys.
|
const sqlPattern = itemsWhitelist.map(p => `(${p})`).join('|');
|
||||||
const keys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
|
const orConds = [`item_key ~* $${i++}`];
|
||||||
if (keys.length === 0) return NextResponse.json([]);
|
values.push(sqlPattern);
|
||||||
conditions.push(`item_key = ANY($${i++})`);
|
if (localeKeys.length > 0) {
|
||||||
values.push(keys);
|
orConds.push(`item_key = ANY($${i++})`);
|
||||||
|
values.push(localeKeys);
|
||||||
|
}
|
||||||
|
conditions.push(`(${orConds.join(' OR ')})`);
|
||||||
} else {
|
} else {
|
||||||
conditions.push(`item_key = ANY($${i++})`);
|
conditions.push(`item_key = ANY($${i++})`);
|
||||||
values.push(itemsWhitelist);
|
values.push(itemsWhitelist);
|
||||||
@@ -43,12 +46,15 @@ export const GET = withAuth(async (req: NextRequest) => {
|
|||||||
if (itemsBlacklist.length > 0) {
|
if (itemsBlacklist.length > 0) {
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
const localeMap = getServerLocaleMap();
|
const localeMap = getServerLocaleMap();
|
||||||
const keys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
|
const localeKeys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
|
||||||
// If blacklist pattern matches nothing, nothing to exclude — skip condition
|
const sqlPattern = itemsBlacklist.map(p => `(${p})`).join('|');
|
||||||
if (keys.length > 0) {
|
const andConds = [`item_key !~* $${i++}`];
|
||||||
conditions.push(`item_key != ALL($${i++})`);
|
values.push(sqlPattern);
|
||||||
values.push(keys);
|
if (localeKeys.length > 0) {
|
||||||
|
andConds.push(`item_key != ALL($${i++})`);
|
||||||
|
values.push(localeKeys);
|
||||||
}
|
}
|
||||||
|
conditions.push(`(${andConds.join(' AND ')})`);
|
||||||
} else {
|
} else {
|
||||||
conditions.push(`item_key != ALL($${i++})`);
|
conditions.push(`item_key != ALL($${i++})`);
|
||||||
values.push(itemsBlacklist);
|
values.push(itemsBlacklist);
|
||||||
@@ -78,10 +84,15 @@ export const GET = withAuth(async (req: NextRequest) => {
|
|||||||
if (itemsWhitelist.length > 0) {
|
if (itemsWhitelist.length > 0) {
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
const localeMap = getServerLocaleMap();
|
const localeMap = getServerLocaleMap();
|
||||||
const keys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
|
const localeKeys = [...new Set(itemsWhitelist.flatMap(p => matchKeys(p, localeMap)))];
|
||||||
if (keys.length === 0) return NextResponse.json([]);
|
const sqlPattern = itemsWhitelist.map(p => `(${p})`).join('|');
|
||||||
baseConditions.push(`item_key = ANY($${j++})`);
|
const orConds = [`item_key ~* $${j++}`];
|
||||||
baseValues.push(keys);
|
baseValues.push(sqlPattern);
|
||||||
|
if (localeKeys.length > 0) {
|
||||||
|
orConds.push(`item_key = ANY($${j++})`);
|
||||||
|
baseValues.push(localeKeys);
|
||||||
|
}
|
||||||
|
baseConditions.push(`(${orConds.join(' OR ')})`);
|
||||||
} else {
|
} else {
|
||||||
baseConditions.push(`item_key = ANY($${j++})`);
|
baseConditions.push(`item_key = ANY($${j++})`);
|
||||||
baseValues.push(itemsWhitelist);
|
baseValues.push(itemsWhitelist);
|
||||||
@@ -90,11 +101,15 @@ export const GET = withAuth(async (req: NextRequest) => {
|
|||||||
if (itemsBlacklist.length > 0) {
|
if (itemsBlacklist.length > 0) {
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
const localeMap = getServerLocaleMap();
|
const localeMap = getServerLocaleMap();
|
||||||
const keys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
|
const localeKeys = [...new Set(itemsBlacklist.flatMap(p => matchKeys(p, localeMap)))];
|
||||||
if (keys.length > 0) {
|
const sqlPattern = itemsBlacklist.map(p => `(${p})`).join('|');
|
||||||
baseConditions.push(`item_key != ALL($${j++})`);
|
const andConds = [`item_key !~* $${j++}`];
|
||||||
baseValues.push(keys);
|
baseValues.push(sqlPattern);
|
||||||
|
if (localeKeys.length > 0) {
|
||||||
|
andConds.push(`item_key != ALL($${j++})`);
|
||||||
|
baseValues.push(localeKeys);
|
||||||
}
|
}
|
||||||
|
baseConditions.push(`(${andConds.join(' AND ')})`);
|
||||||
} else {
|
} else {
|
||||||
baseConditions.push(`item_key != ALL($${j++})`);
|
baseConditions.push(`item_key != ALL($${j++})`);
|
||||||
baseValues.push(itemsBlacklist);
|
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/dist/uPlot.min.css';
|
||||||
import uPlot from 'uplot';
|
import uPlot from 'uplot';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
import { useApp } from '@/lib/context';
|
import { useApp } from '@/lib/context';
|
||||||
import { resolveName } from '@/lib/localization';
|
import { resolveName } from '@/lib/localization';
|
||||||
|
import { getColorMap } from '@/lib/colors';
|
||||||
|
import type { ColorMap } from '@/lib/colors';
|
||||||
import { CardShell } from './CardShell';
|
import { CardShell } from './CardShell';
|
||||||
import { makeYScale, makeAnnotationHooks, makeSignalsSeries, makeSignalsAxes, CURSOR_NO_DRAG } from './plotHelpers';
|
import { makeYScale, makeAnnotationHooks, makeSignalsSeries, makeSignalsAxes, CURSOR_NO_DRAG } from './plotHelpers';
|
||||||
import { buildSeriesData } from './seriesData';
|
import { buildSeriesData } from './seriesData';
|
||||||
@@ -24,6 +27,8 @@ interface Props {
|
|||||||
export default function SignalsChart({ config, rows, sessions, alerts, timeMode, onEdit, onDelete }: Props) {
|
export default function SignalsChart({ config, rows, sessions, alerts, timeMode, onEdit, onDelete }: Props) {
|
||||||
const { localeMap } = useApp();
|
const { localeMap } = useApp();
|
||||||
const locale = typeof navigator !== 'undefined' ? navigator.language : 'de-DE';
|
const locale = typeof navigator !== 'undefined' ? navigator.language : 'de-DE';
|
||||||
|
const [colorMap, setColorMap] = useState<ColorMap>(new Map());
|
||||||
|
useEffect(() => { getColorMap().then(setColorMap); }, []);
|
||||||
|
|
||||||
const { containerRef, legendRef } = usePlot(
|
const { containerRef, legendRef } = usePlot(
|
||||||
(el, w, h, lRef) => {
|
(el, w, h, lRef) => {
|
||||||
@@ -45,7 +50,7 @@ export default function SignalsChart({ config, rows, sessions, alerts, timeMode,
|
|||||||
if (lRef.current) lRef.current.appendChild(legendEl);
|
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),
|
axes: makeSignalsAxes(timeMode, locale),
|
||||||
scales: {
|
scales: {
|
||||||
x: { time: false },
|
x: { time: false },
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
import { useApp } from '@/lib/context';
|
import { useApp } from '@/lib/context';
|
||||||
import { resolveName } from '@/lib/localization';
|
import { resolveName } from '@/lib/localization';
|
||||||
import { formatSI } from '@/lib/formatNumber';
|
import { formatSI } from '@/lib/formatNumber';
|
||||||
|
import { getColorMap, getItemColor } from '@/lib/colors';
|
||||||
|
import type { ColorMap } from '@/lib/colors';
|
||||||
import { CardShell } from './CardShell';
|
import { CardShell } from './CardShell';
|
||||||
import type { ChartConfig, SignalRow } from '@/lib/types';
|
import type { ChartConfig, SignalRow } from '@/lib/types';
|
||||||
|
|
||||||
@@ -13,6 +16,8 @@ interface Props {
|
|||||||
|
|
||||||
export default function TableViz({ config, rows, onEdit, onDelete }: Props) {
|
export default function TableViz({ config, rows, onEdit, onDelete }: Props) {
|
||||||
const { localeMap } = useApp();
|
const { localeMap } = useApp();
|
||||||
|
const [colorMap, setColorMap] = useState<ColorMap>(new Map());
|
||||||
|
useEffect(() => { getColorMap().then(setColorMap); }, []);
|
||||||
|
|
||||||
const latest = new Map<string, { green?: number; red?: number }>();
|
const latest = new Map<string, { green?: number; red?: number }>();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
@@ -37,7 +42,11 @@ export default function TableViz({ config, rows, onEdit, onDelete }: Props) {
|
|||||||
const [combinator, item_key] = key.split('::');
|
const [combinator, item_key] = key.split('::');
|
||||||
return (
|
return (
|
||||||
<tr key={key} className="border-t border-gray-800 hover:bg-gray-800/50">
|
<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>
|
<td className="px-2 py-0.5 text-gray-500">{combinator}</td>
|
||||||
{config.signal_type !== 'red' && (
|
{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'}`}>
|
<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 uPlot from 'uplot';
|
||||||
import type { ChartConfig } from '@/lib/types';
|
import type { ChartConfig } from '@/lib/types';
|
||||||
import { formatSI } from '@/lib/formatNumber';
|
import { formatSI } from '@/lib/formatNumber';
|
||||||
|
import type { ColorMap } from '@/lib/colors';
|
||||||
// --- Color helpers ---
|
import { getItemColor } from '@/lib/colors';
|
||||||
|
|
||||||
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%)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SEMANTIC_GREEN = '#4ade80';
|
const SEMANTIC_GREEN = '#4ade80';
|
||||||
const SEMANTIC_RED = '#f87171';
|
const SEMANTIC_RED = '#f87171';
|
||||||
@@ -24,10 +13,11 @@ export interface SeriesStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getSeriesStyle(
|
export function getSeriesStyle(
|
||||||
key: string,
|
key: string,
|
||||||
uCombs: number,
|
uCombs: number,
|
||||||
uItems: number,
|
uItems: number,
|
||||||
uSigs: number,
|
uSigs: number,
|
||||||
|
colorMap: ColorMap = new Map(),
|
||||||
): SeriesStyle {
|
): SeriesStyle {
|
||||||
const [combinator, item_key, sig] = key.split('::');
|
const [combinator, item_key, sig] = key.split('::');
|
||||||
|
|
||||||
@@ -35,9 +25,9 @@ export function getSeriesStyle(
|
|||||||
return { color: sig === 'green' ? SEMANTIC_GREEN : SEMANTIC_RED, dash: undefined };
|
return { color: sig === 'green' ? SEMANTIC_GREEN : SEMANTIC_RED, dash: undefined };
|
||||||
}
|
}
|
||||||
if (uItems > 1) {
|
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[],
|
keys: string[],
|
||||||
timeMode: 'real' | 'tick',
|
timeMode: 'real' | 'tick',
|
||||||
resolveName: (key: string) => string,
|
resolveName: (key: string) => string,
|
||||||
|
colorMap: ColorMap = new Map(),
|
||||||
): uPlot.Series[] {
|
): uPlot.Series[] {
|
||||||
const uCombs = new Set(keys.map(k => k.split('::')[0])).size;
|
const uCombs = new Set(keys.map(k => k.split('::')[0])).size;
|
||||||
const uItems = new Set(keys.map(k => k.split('::')[1])).size;
|
const uItems = new Set(keys.map(k => k.split('::')[1])).size;
|
||||||
@@ -154,7 +145,7 @@ export function makeSignalsSeries(
|
|||||||
xSeries,
|
xSeries,
|
||||||
...keys.map(k => {
|
...keys.map(k => {
|
||||||
const [, item_key] = k.split('::');
|
const [, item_key] = k.split('::');
|
||||||
const { color, dash } = getSeriesStyle(k, uCombs, uItems, uSigs);
|
const { color, dash } = getSeriesStyle(k, uCombs, uItems, uSigs, colorMap);
|
||||||
return {
|
return {
|
||||||
label: getSeriesLabel(k, uCombs, uItems, uSigs, resolveName(item_key)),
|
label: getSeriesLabel(k, uCombs, uItems, uSigs, resolveName(item_key)),
|
||||||
stroke: color,
|
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