-[](https://github.com/danielcopper/decky-romm-sync/actions/workflows/ci.yml)
-[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
-[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
-[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
-[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
-[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
-[](https://github.com/danielcopper/decky-romm-sync/releases)
+
# decky-romm-sync
-A [Decky Loader](https://decky.xyz/) plugin that syncs your self-hosted [RomM](https://github.com/rommapp/romm) library
-into Steam as non-steam shortcuts. Games appear directly in your Steam library, launch through
-[RetroDECK](https://retrodeck.net/), and keep their saves in sync across devices through your RomM server.
+## Your self-hosted RomM library, in your Steam library
+
+[Getting Started](https://danielcopper.github.io/decky-romm-sync/user-guide/getting-started/) ·
+[Configuration](https://danielcopper.github.io/decky-romm-sync/user-guide/configuration/) ·
+[Syncing](https://danielcopper.github.io/decky-romm-sync/user-guide/syncing-your-library/) ·
+[Managing Games](https://danielcopper.github.io/decky-romm-sync/user-guide/managing-games/)
+
+[BIOS & Cores](https://danielcopper.github.io/decky-romm-sync/user-guide/bios-management/) ·
+[Save Sync](https://danielcopper.github.io/decky-romm-sync/user-guide/save-sync/) ·
+[Troubleshooting](https://danielcopper.github.io/decky-romm-sync/user-guide/troubleshooting/)
+
+
+
+
+
+
+
-## 📖 [Read the full documentation →](https://danielcopper.github.io/decky-romm-sync/)
+
-Installation, setup, save sync, BIOS management, troubleshooting, and the architecture reference all live on the
-documentation site. This README is just the quick tour.
+A [Decky Loader](https://decky.xyz/) plugin that syncs your self-hosted [RomM](https://github.com/rommapp/romm) library
+into Steam as non-steam shortcuts. Games appear directly in your Steam library, launch through
+[RetroDECK](https://retrodeck.net/), and can keep their saves in sync across devices through your RomM server.
## Features
-- **Library sync** — Pulls platforms and ROMs from your RomM server and creates Steam shortcuts, complete with cover
- art, hero banners, and logos (with optional [SteamGridDB](https://www.steamgriddb.com/) artwork)
-- **Save sync** — Keeps save files in sync across devices through your RomM server, with newest-wins conflict resolution
- and a manual override when you need it
-- **ROM downloads** — Download ROMs on demand with progress tracking and a managed download queue
-- **BIOS management** — Download firmware/BIOS files from RomM for systems that need them (PSX, Dreamcast, PS2, …)
-- **Game detail page** — Install status, BIOS status, and download/uninstall actions right on each game's Steam page
-- **Per-platform control** — Choose exactly which platforms get synced
-- **Controller friendly** — Full gamepad navigation throughout the plugin UI
-- **Steam Input config** — Per-shortcut Steam Input mode (Default / Force On / Force Off)
-- **RetroDECK path migration** — Move your RetroDECK installation between storage locations without re-syncing
-- **RetroArch diagnostics** — Detects misconfigured input drivers that break menu navigation
+- **Library sync** — Pulls the platforms and collections you pick from your RomM server into Steam as non-steam
+ shortcuts with RomM cover art; later runs are incremental and preview what will change before touching anything
+- **SteamGridDB artwork** — Add a free [SteamGridDB](https://www.steamgriddb.com/) key to get hero banners, logos, wide
+ capsules and custom icons, with a manual picker for games that don't match automatically
+- **Save sync** — Opt-in save syncing across devices through your RomM server, automatically before launch and after you
+ quit; identical saves resolve silently, and if both sides genuinely changed you decide which one wins
+- **Save slots & version history** — Multiple named save profiles per game, plus per-file version history with restore
+- **ROM downloads** — Download on demand with progress, pause/resume/cancel, and a managed queue
+- **BIOS management** — Per-platform BIOS status, download all or only what your active core requires, hash-verified
+ against a bundled registry, and delete them again when you're done
+- **Game detail page** — Replaces Steam's page for synced games: RomM metadata, RetroAchievements progress, playtime,
+ install and BIOS status, save management, and per-game actions
+- **Multi-disc & multi-version** — Pick the disc for multi-disc games and switch between regions or revisions of the
+ same game, right from its Steam page
+- **Emulator cores** — Set the core per system, or override it for a single game
+- **Steam Input** — Pick a Steam Input mode (Default / Force On / Force Off) and apply it to every shortcut the plugin
+ created
+- **RetroArch input fix** — Spots the `input_driver` value that breaks controller navigation in RetroArch's menus and
+ repairs it in one tap
+- **Follows RetroDECK moves** — Moved RetroDECK to another drive? The ROMs, BIOS files and saves the plugin manages are
+ relocated and your shortcuts repointed — nothing needs re-downloading
+- **Cleanup tools** — Remove shortcuts per platform or all at once, uninstall ROMs, and clear orphaned grid images
## Screenshots
-| QAM panel | Game detail page |
-| :----------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: |
-|  |  |
-| **BIOS management** | **Per-game actions** |
-|  |  |
+| Quick Access panel | Game detail page |
+| :----------------------------------------------: | :----------------------------------------------------: |
+|  |  |
+| **BIOS management** | **Per-game actions** |
+|  |  |
## Requirements
@@ -52,7 +71,8 @@ documentation site. This README is just the quick tour.
## Installation
-### From the Decky Store
+
+From the Decky Store — not available yet
> ⚠️ **Not available yet.** The plugin will be submitted to the [Decky Store](https://plugins.deckbrew.xyz/) with the
> **v1.0** release. Until then, use the manual install below.
@@ -60,7 +80,10 @@ documentation site. This README is just the quick tour.
Once published, install it straight from Decky's built-in store — open the Quick Access Menu → **Decky** → store icon,
search for **RomM Sync**, and install. No Developer Mode required.
-### From ZIP or URL
+
+
+
+From ZIP or URL — the current method
This is the current method while v1.0 is in progress. It requires **Developer Mode** in Decky Loader (Decky settings →
gear icon → toggle **Developer Mode**).
@@ -70,6 +93,8 @@ gear icon → toggle **Developer Mode**).
2. In Decky settings → **Developer** tab → **Install Plugin from ZIP** (or **from URL** with the
[latest release link](https://github.com/danielcopper/decky-romm-sync/releases/latest/download/decky-romm-sync.zip))
+
+
> Full step-by-step instructions, including first-time setup, are in
> [Getting Started](https://danielcopper.github.io/decky-romm-sync/user-guide/getting-started/).
@@ -93,6 +118,13 @@ Build from source, run the tests, and read the architecture reference on the doc
UI into a windowed Big Picture on the Deck, no Game Mode switching
- [Backend architecture](https://danielcopper.github.io/decky-romm-sync/architecture/backend-architecture/)
+[](https://github.com/danielcopper/decky-romm-sync/actions/workflows/ci.yml)
+[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
+[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
+[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
+[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
+[](https://sonarcloud.io/summary/new_code?id=danielcopper_decky-romm-sync)
+
## Acknowledgments
This plugin stands on the shoulders of some great projects:
@@ -116,4 +148,5 @@ This plugin stands on the shoulders of some great projects:
## License
-GPL-3.0
+GPL-3.0. This is an independent project and is not affiliated with, endorsed by, or sponsored by the RomM project or
+Valve Corporation.
diff --git a/assets/banner.png b/assets/banner.png
deleted file mode 100644
index 73933255..00000000
Binary files a/assets/banner.png and /dev/null differ
diff --git a/assets/logo.png b/assets/logo.png
new file mode 100644
index 00000000..f6970acc
Binary files /dev/null and b/assets/logo.png differ
diff --git a/assets/logo.svg b/assets/logo.svg
new file mode 100644
index 00000000..57709c3e
--- /dev/null
+++ b/assets/logo.svg
@@ -0,0 +1 @@
+
diff --git a/assets/store_image.png b/assets/store_image.png
index f8534422..1daf5d3b 100644
Binary files a/assets/store_image.png and b/assets/store_image.png differ
diff --git a/docs/assets/banner.png b/docs/assets/banner.png
deleted file mode 100644
index 73933255..00000000
Binary files a/docs/assets/banner.png and /dev/null differ
diff --git a/docs/assets/logo.png b/docs/assets/logo.png
new file mode 100644
index 00000000..f6970acc
Binary files /dev/null and b/docs/assets/logo.png differ
diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg
new file mode 100644
index 00000000..57709c3e
--- /dev/null
+++ b/docs/assets/logo.svg
@@ -0,0 +1 @@
+
diff --git a/docs/index.md b/docs/index.md
index e7ec3cd0..fb8e2f27 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,4 +1,4 @@
-
+
# decky-romm-sync
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
new file mode 100644
index 00000000..1e8d8a2d
--- /dev/null
+++ b/docs/stylesheets/extra.css
@@ -0,0 +1,20 @@
+/* Site palette, taken from the plugin mark: a deep Steam-blue shell with a
+ lighter blue for interactive accents. Material's named palettes have nothing
+ in this range, so both schemes are set as custom colours. */
+
+[data-md-color-scheme="default"] {
+ --md-primary-fg-color: #2d5876;
+ --md-primary-fg-color--light: #4795c9;
+ --md-primary-fg-color--dark: #1b3549;
+ --md-accent-fg-color: #2a6f9e;
+ --md-accent-fg-color--transparent: #2a6f9e1a;
+}
+
+[data-md-color-scheme="slate"] {
+ --md-primary-fg-color: #1a3549;
+ --md-primary-fg-color--light: #2d5876;
+ --md-primary-fg-color--dark: #12222f;
+ --md-accent-fg-color: #66c0f4;
+ --md-accent-fg-color--transparent: #66c0f41a;
+ --md-typeset-a-color: #8fd0f7;
+}
diff --git a/mkdocs.yml b/mkdocs.yml
index fc110082..05cdefcd 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,5 +1,5 @@
site_name: decky-romm-sync
-site_description: Decky Loader plugin to sync your RomM library to Steam as non-steam shortcuts
+site_description: A Decky Loader plugin that syncs your self-hosted RomM library into Steam
site_url: https://danielcopper.github.io/decky-romm-sync/
# Architectural Decision Records (`docs/adr/`) are kept in the repo for
@@ -15,18 +15,20 @@ edit_uri: edit/main/docs/
theme:
name: material
+ logo: assets/logo.svg
+ favicon: assets/logo.png
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
- primary: deep purple
- accent: amber
+ primary: custom
+ accent: custom
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
- primary: deep purple
- accent: amber
+ primary: custom
+ accent: custom
toggle:
icon: material/brightness-4
name: Switch to light mode
@@ -39,6 +41,9 @@ theme:
- content.action.edit
- content.code.copy
+extra_css:
+ - stylesheets/extra.css
+
plugins:
- search
diff --git a/scripts/logo/README.md b/scripts/logo/README.md
new file mode 100644
index 00000000..308355c7
--- /dev/null
+++ b/scripts/logo/README.md
@@ -0,0 +1,31 @@
+# Logo
+
+`gen.py` draws the plugin mark — a handheld's right-hand half set in a disc, split along a 45° facet that runs unbroken
+across disc, body, screen and every control.
+
+## Rendering
+
+```sh
+python3 gen.py > sheet.svg
+rsvg-convert -w 1300 sheet.svg -o preview.png
+```
+
+The sheet shows the candidate palettes side by side, each on a dark ground (plugin store, quick-access menu) and a light
+one (docs site), plus 64px and 32px so small-size legibility stays visible while tweaking.
+
+## Changing things
+
+- **Palettes** — `PALETTES`, one row per candidate: disc pair, body pair, screen pair, and which optional details to
+ include. Every pair is (above-left, below-right) for the facet.
+- **Positions and sizes** — the block of `*_GEOM` constants. These are on a millimetre grid mapped into drawing units by
+ `MM`, `X0` and `Y0`, so moving something means changing a measurement rather than guessing at coordinates.
+- **Outline and groove** — `deck_path.txt` and `seam_path.txt`, plain SVG path data. Regenerate them only if the shape
+ itself changes; day-to-day work does not touch them.
+
+## Notes
+
+Two things are deliberately drawn heavier than their real proportions, because at icon sizes a faithful version
+disappears: the rim inside the thumbstick, and the groove parting the grip (`SEAM_W`).
+
+Controls are sized by their caps, not by the recesses they sit in. The recess is noticeably wider and makes the mark
+look clumsy — the thumbstick is the clearest case, where the difference is a third of the diameter.
diff --git a/scripts/logo/deck_path.txt b/scripts/logo/deck_path.txt
new file mode 100644
index 00000000..1f5521b1
--- /dev/null
+++ b/scripts/logo/deck_path.txt
@@ -0,0 +1 @@
+M 8.00 39.48 L 127.32 39.48 L 138.42 40.03 L 141.26 40.30 L 145.39 41.14 L 147.97 42.03 L 150.23 43.25 L 152.58 45.16 L 154.57 47.49 L 156.13 50.06 L 157.07 52.39 L 157.81 55.23 L 158.24 59.10 L 158.75 77.16 L 158.68 83.61 L 158.81 85.68 L 158.81 94.71 L 158.29 106.84 L 158.29 114.84 L 157.49 124.90 L 156.74 130.32 L 155.86 134.97 L 155.05 138.32 L 154.12 141.16 L 152.66 144.52 L 151.34 146.84 L 149.90 148.83 L 147.84 151.12 L 146.53 152.32 L 144.13 154.10 L 141.90 155.30 L 139.35 156.37 L 134.57 157.78 L 130.55 159.27 L 127.32 159.74 L 121.13 160.27 L 120.87 160.52 L 89.65 160.52 L 89.39 160.75 L 88.87 160.77 L 62.55 160.77 L 62.03 160.75 L 61.77 160.52 L 45.77 160.52 L 8.00 160.52 Z
\ No newline at end of file
diff --git a/scripts/logo/gen.py b/scripts/logo/gen.py
new file mode 100644
index 00000000..deab7861
--- /dev/null
+++ b/scripts/logo/gen.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python3
+"""Generates the plugin mark: a handheld's right-hand half, set in a disc.
+
+Disc, body, display and every control are split along the anti-diagonal
+(x + y = 200), so the facet edge runs unbroken across the whole mark, lighter
+above-left and darker below-right. Emits an SVG contact sheet of the candidate
+palettes on both a dark and a light ground, plus 64px and 32px sizes.
+
+The outline and the grip groove are stored as ready-made paths next to this
+file. Everything else is laid out on a millimetre grid and mapped into the
+drawing through one constant, so a position can be nudged by changing a number
+that still means something.
+"""
+
+import json
+import pathlib
+
+SHEET_BG = "#0e1116"
+LIGHT_BG = "#e9eef3"
+
+_HERE = pathlib.Path(__file__).parent
+BODY = (_HERE / "deck_path.txt").read_text().strip()
+LOWER_RIGHT = "300,-100 300,300 -100,300"
+
+# The millimetre grid the inner geometry is laid out on. Anchored so that a
+# 117mm-tall body spans 39.46..160.80 in drawing units (1.0371 units/mm) and its
+# right edge, at 149mm, lands on 158.80.
+MM = 1.0371
+X0, Y0 = 4.27, 100.13
+
+
+def mmx(v):
+ return X0 + MM * v
+
+
+def mmy(v):
+ return Y0 - MM * v
+
+
+def mm_rect(x_mm, y_mm, w_mm, h_mm, r_mm):
+ """Centre-based rect in mm -> logo-space (x, y, w, h, rx)."""
+ return (
+ mmx(x_mm - w_mm / 2),
+ mmy(y_mm + h_mm / 2),
+ w_mm * MM,
+ h_mm * MM,
+ r_mm * MM,
+ )
+
+
+# Screen 173.7 x 106.7, centred; its left half runs out of the disc and clips.
+# Every control is sized by its cap, not by the recess it sits in — the recess
+# is about 0.3mm wider all round and reads visibly too large.
+DSP_GEOM = mm_rect(0, 0, 173.7, 106.7, 3.0)
+PAD_GEOM = mm_rect(105.785, 1.335, 32.5, 32.5, 4.375)
+DOTS_GEOM = mm_rect(97.655, -24.09, 16.25, 6.5, 3.25)
+# Thumbstick. The cap is r=9.309; the moulded recess around it is r=14.419 and
+# is deliberately not drawn, since it reads as an oversized stick. The ring is
+# the rim of the cap's dished top, drawn heavier than life so it survives small.
+STK_GEOM = (mmx(102.83), mmy(34.50), 9.309 * MM)
+STK_RING_OUT = (STK_GEOM[0], STK_GEOM[1], 6.55 * MM)
+STK_RING_IN = (STK_GEOM[0], STK_GEOM[1], 5.35 * MM)
+RING_T = ("#1b3549", "#0e2233")
+
+# The groove parting the grip. Runs edge to edge, from the right-hand edge round
+# to the bottom. Real width is well under a millimetre, which would be a hairline
+# at icon sizes, so the stroke is drawn heavier.
+SEAM = (_HERE / "seam_path.txt").read_text().strip()
+
+
+# The wordmark is stored as outlines so no font has to be present to render it.
+# It is loaded on demand because it depends on the project name, which the mark
+# itself does not.
+def _wordmark():
+ return (
+ (_HERE / "wordmark_path.txt").read_text().strip(),
+ json.loads((_HERE / "wordmark_meta.json").read_text()),
+ )
+
+
+SEAM_T = ("#c6d9e8", "#a6bfd4") # same light pair as the controls
+SEAM_W = 1.0
+
+# Menu key.
+MENU_GEOM = mm_rect(115.125, 51.85, 9.52, 4.23, 2.115)
+
+# Speaker grille: slots, not holes. Each row is given as end-cap pairs, so the
+# rows read 4 / (1 long + 2) / 3 openings. Rows run shorter towards the bottom,
+# following the shell's edge.
+_SLOT_R = 0.847
+_SLOT_ROWS = [
+ (-34.03, [(90.18, 92.412), (94.647, 96.879), (99.115, 101.346), (103.582, 105.814)]),
+ (-37.23, [(90.18, 94.646), (96.882, 99.113), (101.349, 103.581)]),
+ (-40.44, [(90.18, 92.413), (94.648, 96.88), (99.116, 101.347)]),
+]
+SPEAKER_GEOMS = [
+ mm_rect((x1 + x2) / 2, y, (x2 - x1) + 2 * _SLOT_R, 2 * _SLOT_R, _SLOT_R)
+ for y, slots in _SLOT_ROWS
+ for x1, x2 in slots
+]
+
+DISC_C = ("#aec6da", "#93b0c8")
+DISC_D = ("#94b0c8", "#7a99b4")
+BODY_T = ("#2d5876", "#1a3549")
+DSP_DARK = ("#12222f", "#0a1621")
+DSP_LIGHT = ("#d3e3ef", "#b0c8db")
+PAD_T = ("#c6d9e8", "#a6bfd4")
+
+_ALL = ("menu", "spk")
+
+# The chosen mark.
+FINAL = ("final", DISC_C, BODY_T, DSP_DARK, _ALL)
+
+# Kept so alternatives stay one edit away rather than a re-derivation.
+PALETTES = [
+ FINAL,
+ ("disc darker", DISC_D, BODY_T, DSP_DARK, _ALL),
+ ("screen light", DISC_C, BODY_T, DSP_LIGHT, _ALL),
+ ("both", DISC_D, BODY_T, DSP_LIGHT, _ALL),
+]
+
+
+def _rect(geom, fill=None):
+ x, y, w, h, r = geom
+ attrs = f'x="{x}" y="{y}" width="{w}" height="{h}" rx="{r}"'
+ return f'' if fill else f""
+
+
+def _circle(geom, fill=None):
+ cx, cy, r = geom
+ attrs = f'cx="{cx}" cy="{cy}" r="{r}"'
+ return f'' if fill else f""
+
+
+# ABXY diamond, sized by the button cap.
+ABXY_GEOMS = [
+ (mmx(x), mmy(y), 4.366 * MM) for x, y in ((131.71, 48.34), (122.95, 39.59), (140.42, 39.59), (131.71, 30.84))
+]
+
+
+def _faceted(uid, geom, tones, shape=_rect):
+ """A shape carrying the same diagonal split as everything else."""
+ return (
+ f'{shape(geom)}'
+ f"{shape(geom, tones[0])}"
+ f''
+ f''
+ f""
+ )
+
+
+def _seam(uid):
+ """The groove, stroked, carrying the diagonal split like every filled shape."""
+ stroke = f'stroke-width="{SEAM_W}" fill="none" stroke-linecap="round" stroke-linejoin="round"'
+ return (
+ f''
+ f''
+ f''
+ f''
+ f""
+ )
+
+
+def mark(uid, pal):
+ _, disc, body, dsp, extras = pal
+ parts = [
+ _seam(uid),
+ _faceted(f"dsp{uid}", DSP_GEOM, dsp),
+ _faceted(f"pad{uid}", PAD_GEOM, PAD_T),
+ _faceted(f"dot{uid}", DOTS_GEOM, PAD_T),
+ _faceted(f"stk{uid}", STK_GEOM, PAD_T, _circle),
+ _faceted(f"rgo{uid}", STK_RING_OUT, RING_T, _circle),
+ _faceted(f"rgi{uid}", STK_RING_IN, PAD_T, _circle),
+ ]
+ parts += [_faceted(f"ab{i}{uid}", g, PAD_T, _circle) for i, g in enumerate(ABXY_GEOMS)]
+ if "menu" in extras:
+ parts.append(_faceted(f"mnu{uid}", MENU_GEOM, PAD_T))
+ if "spk" in extras:
+ parts += [_faceted(f"sp{i}{uid}", g, PAD_T) for i, g in enumerate(SPEAKER_GEOMS)]
+ inner = "".join(parts)
+ return (
+ f""
+ f''
+ f''
+ f""
+ f''
+ f''
+ f''
+ f''
+ f''
+ f''
+ f""
+ f''
+ f"{inner}"
+ f""
+ f""
+ )
+
+
+def sheet():
+ cols, tile, gap, pad = len(PALETTES), 200, 46, 40
+ w = pad * 2 + cols * tile + (cols - 1) * gap
+ h = 660
+ p = [
+ f'")
+ return "\n".join(p)
+
+
+# Lockup: mark above, wordmark below. The wordmark is set a little wider than
+# the disc so the pair reads as one block rather than a circle with a caption.
+LOCK_PAD = 10.0
+LOCK_GAP = 26.0
+WM_TO_DISC = 1.35 # wordmark width relative to the disc's diameter
+
+
+def lockup(pal=FINAL, ink="#1b3549", size=None):
+ wordmark, _WM = _wordmark()
+ disc_d = 168.0
+ wm_w = disc_d * WM_TO_DISC
+ s = wm_w / (_WM["bbox"][2] - _WM["bbox"][0])
+ cap = -_WM["bbox"][1] * s
+ w = wm_w + 2 * LOCK_PAD
+ h = LOCK_PAD + disc_d + LOCK_GAP + cap + LOCK_PAD
+ mark_dx = w / 2 - 100.0
+ mark_dy = LOCK_PAD - 16.0 # the disc starts at y=16 inside the 200-unit mark
+ base_y = LOCK_PAD + disc_d + LOCK_GAP + cap
+ wm_x = LOCK_PAD - _WM["bbox"][0] * s
+ width = f' width="{size}"' if size else ""
+ return (
+ f''
+ )
+
+
+def standalone(pal=FINAL, size=512):
+ """The mark on its own, transparent outside the disc — the shipped asset."""
+ return (
+ f''
+ )
+
+
+if __name__ == "__main__":
+ import sys
+
+ if "--lockup" in sys.argv:
+ ink = sys.argv[sys.argv.index("--lockup") + 1]
+ print(lockup(ink=ink))
+ elif "--asset" in sys.argv:
+ print(standalone())
+ else:
+ print(sheet())
diff --git a/scripts/logo/preview.png b/scripts/logo/preview.png
new file mode 100644
index 00000000..1fd893b9
Binary files /dev/null and b/scripts/logo/preview.png differ
diff --git a/scripts/logo/seam_path.txt b/scripts/logo/seam_path.txt
new file mode 100644
index 00000000..83b67a57
--- /dev/null
+++ b/scripts/logo/seam_path.txt
@@ -0,0 +1 @@
+M 158.80 81.48 L 158.52 81.48 L 158.06 81.48 L 157.37 81.48 L 156.45 81.48 L 155.32 81.48 L 154.09 81.48 L 152.86 81.48 L 151.66 81.48 L 150.41 81.48 L 148.93 81.48 L 146.88 81.48 L 146.01 81.48 L 145.26 81.54 L 144.44 81.73 L 143.71 82.01 L 143.05 82.38 L 142.46 82.82 L 141.93 83.34 L 141.46 83.94 L 141.06 84.63 L 140.76 85.40 L 140.57 86.23 L 140.50 86.98 L 140.50 88.37 L 140.50 90.77 L 140.50 92.77 L 140.50 95.51 L 140.50 99.07 L 140.50 102.49 L 140.50 108.14 L 140.49 109.74 L 140.30 111.92 L 139.87 114.07 L 139.21 116.16 L 138.33 118.16 L 137.24 120.06 L 136.67 120.89 L 135.35 122.77 L 133.81 124.97 L 132.38 127.02 L 130.82 129.25 L 128.49 132.57 L 126.06 136.04 L 124.29 138.57 L 120.83 143.51 L 118.35 147.06 L 116.61 149.55 L 115.35 151.34 L 114.42 152.67 L 113.66 153.76 L 113.05 154.62 L 112.82 154.96 L 112.36 155.61 L 111.92 156.25 L 111.72 156.52 L 111.39 157.06 L 111.10 157.66 L 110.88 158.27 L 110.73 158.83 L 110.65 159.25 L 110.61 159.60 L 110.59 159.72 L 110.58 159.97 L 110.57 160.19 L 110.57 160.30 L 110.57 160.52 L 110.57 160.68 L 110.57 160.74 L 110.57 160.42 L 110.57 160.26 L 110.57 160.26 L 110.57 160.26
\ No newline at end of file