mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-22 10:02:17 -05:00
Implements the search feature for client_v2: a new backend search endpoint plus a results-panel UI, replacing the old query box that only filtered the library list by filament name. Backend (additive, API v1 backward compatibility preserved): - New GET /api/v1/search returning categorized spool/filament/vendor results, each annotated with the field that matched, plus an is_color_query flag. - Case-insensitive text search over native text fields and text/choice extra fields; exact spool-id match for numeric queries; color-similarity search (CSS color name or hex) via find_by_color, ordered closest first. - spoolman/colors.py resolves the CSS4 color names / hex codes to hex. - Integration tests (run across SQLite/PostgreSQL/MySQL/CockroachDB in CI). Frontend (client_v2): - SearchBox overlay with Spools/Filaments/Manufacturers sections, per-result match badges, keyboard navigation, a color-similarity threshold slider (persisted to localStorage), and an English-only color-name hint. - Clicking a result opens the matching inspector on the Library page. - Removes the old q -> filament.name inline list filtering. Claude-Session: https://claude.ai/code/session_01Ne7RsfHejKZX97uAq3aVMn
182 lines
5.1 KiB
Python
182 lines
5.1 KiB
Python
"""CSS named colors and color-string resolution for the search feature.
|
|
|
|
Color names are the standard CSS Color Module Level 4 keywords, which are
|
|
English-only. ``resolve_color`` turns a user-supplied color string (a hex code or
|
|
one of these names) into a canonical ``RRGGBB`` hex string suitable for
|
|
``spoolman.math.hex_to_rgb``; it returns ``None`` when the string is not a color.
|
|
"""
|
|
|
|
import re
|
|
|
|
# CSS Color Module Level 4 named colors (148 keywords), lowercased name -> RRGGBB.
|
|
CSS_COLORS: dict[str, str] = {
|
|
"aliceblue": "f0f8ff",
|
|
"antiquewhite": "faebd7",
|
|
"aqua": "00ffff",
|
|
"aquamarine": "7fffd4",
|
|
"azure": "f0ffff",
|
|
"beige": "f5f5dc",
|
|
"bisque": "ffe4c4",
|
|
"black": "000000",
|
|
"blanchedalmond": "ffebcd",
|
|
"blue": "0000ff",
|
|
"blueviolet": "8a2be2",
|
|
"brown": "a52a2a",
|
|
"burlywood": "deb887",
|
|
"cadetblue": "5f9ea0",
|
|
"chartreuse": "7fff00",
|
|
"chocolate": "d2691e",
|
|
"coral": "ff7f50",
|
|
"cornflowerblue": "6495ed",
|
|
"cornsilk": "fff8dc",
|
|
"crimson": "dc143c",
|
|
"cyan": "00ffff",
|
|
"darkblue": "00008b",
|
|
"darkcyan": "008b8b",
|
|
"darkgoldenrod": "b8860b",
|
|
"darkgray": "a9a9a9",
|
|
"darkgreen": "006400",
|
|
"darkgrey": "a9a9a9",
|
|
"darkkhaki": "bdb76b",
|
|
"darkmagenta": "8b008b",
|
|
"darkolivegreen": "556b2f",
|
|
"darkorange": "ff8c00",
|
|
"darkorchid": "9932cc",
|
|
"darkred": "8b0000",
|
|
"darksalmon": "e9967a",
|
|
"darkseagreen": "8fbc8f",
|
|
"darkslateblue": "483d8b",
|
|
"darkslategray": "2f4f4f",
|
|
"darkslategrey": "2f4f4f",
|
|
"darkturquoise": "00ced1",
|
|
"darkviolet": "9400d3",
|
|
"deeppink": "ff1493",
|
|
"deepskyblue": "00bfff",
|
|
"dimgray": "696969",
|
|
"dimgrey": "696969",
|
|
"dodgerblue": "1e90ff",
|
|
"firebrick": "b22222",
|
|
"floralwhite": "fffaf0",
|
|
"forestgreen": "228b22",
|
|
"fuchsia": "ff00ff",
|
|
"gainsboro": "dcdcdc",
|
|
"ghostwhite": "f8f8ff",
|
|
"gold": "ffd700",
|
|
"goldenrod": "daa520",
|
|
"gray": "808080",
|
|
"green": "008000",
|
|
"greenyellow": "adff2f",
|
|
"grey": "808080",
|
|
"honeydew": "f0fff0",
|
|
"hotpink": "ff69b4",
|
|
"indianred": "cd5c5c",
|
|
"indigo": "4b0082",
|
|
"ivory": "fffff0",
|
|
"khaki": "f0e68c",
|
|
"lavender": "e6e6fa",
|
|
"lavenderblush": "fff0f5",
|
|
"lawngreen": "7cfc00",
|
|
"lemonchiffon": "fffacd",
|
|
"lightblue": "add8e6",
|
|
"lightcoral": "f08080",
|
|
"lightcyan": "e0ffff",
|
|
"lightgoldenrodyellow": "fafad2",
|
|
"lightgray": "d3d3d3",
|
|
"lightgreen": "90ee90",
|
|
"lightgrey": "d3d3d3",
|
|
"lightpink": "ffb6c1",
|
|
"lightsalmon": "ffa07a",
|
|
"lightseagreen": "20b2aa",
|
|
"lightskyblue": "87cefa",
|
|
"lightslategray": "778899",
|
|
"lightslategrey": "778899",
|
|
"lightsteelblue": "b0c4de",
|
|
"lightyellow": "ffffe0",
|
|
"lime": "00ff00",
|
|
"limegreen": "32cd32",
|
|
"linen": "faf0e6",
|
|
"magenta": "ff00ff",
|
|
"maroon": "800000",
|
|
"mediumaquamarine": "66cdaa",
|
|
"mediumblue": "0000cd",
|
|
"mediumorchid": "ba55d3",
|
|
"mediumpurple": "9370db",
|
|
"mediumseagreen": "3cb371",
|
|
"mediumslateblue": "7b68ee",
|
|
"mediumspringgreen": "00fa9a",
|
|
"mediumturquoise": "48d1cc",
|
|
"mediumvioletred": "c71585",
|
|
"midnightblue": "191970",
|
|
"mintcream": "f5fffa",
|
|
"mistyrose": "ffe4e1",
|
|
"moccasin": "ffe4b5",
|
|
"navajowhite": "ffdead",
|
|
"navy": "000080",
|
|
"oldlace": "fdf5e6",
|
|
"olive": "808000",
|
|
"olivedrab": "6b8e23",
|
|
"orange": "ffa500",
|
|
"orangered": "ff4500",
|
|
"orchid": "da70d6",
|
|
"palegoldenrod": "eee8aa",
|
|
"palegreen": "98fb98",
|
|
"paleturquoise": "afeeee",
|
|
"palevioletred": "db7093",
|
|
"papayawhip": "ffefd5",
|
|
"peachpuff": "ffdab9",
|
|
"peru": "cd853f",
|
|
"pink": "ffc0cb",
|
|
"plum": "dda0dd",
|
|
"powderblue": "b0e0e6",
|
|
"purple": "800080",
|
|
"rebeccapurple": "663399",
|
|
"red": "ff0000",
|
|
"rosybrown": "bc8f8f",
|
|
"royalblue": "4169e1",
|
|
"saddlebrown": "8b4513",
|
|
"salmon": "fa8072",
|
|
"sandybrown": "f4a460",
|
|
"seagreen": "2e8b57",
|
|
"seashell": "fff5ee",
|
|
"sienna": "a0522d",
|
|
"silver": "c0c0c0",
|
|
"skyblue": "87ceeb",
|
|
"slateblue": "6a5acd",
|
|
"slategray": "708090",
|
|
"slategrey": "708090",
|
|
"snow": "fffafa",
|
|
"springgreen": "00ff7f",
|
|
"steelblue": "4682b4",
|
|
"tan": "d2b48c",
|
|
"teal": "008080",
|
|
"thistle": "d8bfd8",
|
|
"tomato": "ff6347",
|
|
"turquoise": "40e0d0",
|
|
"violet": "ee82ee",
|
|
"wheat": "f5deb3",
|
|
"white": "ffffff",
|
|
"whitesmoke": "f5f5f5",
|
|
"yellow": "ffff00",
|
|
"yellowgreen": "9acd32",
|
|
}
|
|
|
|
_HEX_RE = re.compile(r"^#?([0-9a-f]{3}|[0-9a-f]{6})$")
|
|
|
|
|
|
def resolve_color(query: str) -> str | None:
|
|
"""Resolve a color string to a canonical ``RRGGBB`` hex, or ``None``.
|
|
|
|
Accepts a hex code (``#rgb``, ``#rrggbb`` or the same without ``#``) or a CSS
|
|
color name (case-insensitive, surrounding/internal whitespace ignored).
|
|
"""
|
|
normalized = query.strip().lower()
|
|
|
|
match = _HEX_RE.match(normalized.replace(" ", ""))
|
|
if match is not None:
|
|
hex_digits = match.group(1)
|
|
if len(hex_digits) == 3: # noqa: PLR2004 - expand shorthand #rgb -> #rrggbb
|
|
hex_digits = "".join(ch * 2 for ch in hex_digits)
|
|
return hex_digits
|
|
|
|
return CSS_COLORS.get(normalized.replace(" ", ""))
|