Skip to content

Legacy Importer Internals

Bobby Comet edited this page Aug 2, 2026 · 4 revisions

Legacy Importer Internals & Conversion Rules

Project → Import Legacy Theme… builds a Studio project graph from a theme folder. Conversion is semantic, not a pixel-perfect clone. This page documents how that mapping works.

Source of truth in code: conkystudio/importer/legacy_parser.py (UI entry: main_window._import_legacy_theme). Related behaviour also lives in visuals.py / sources_external.py / lua_gen.py / builder.py / start_sh_gen.py for Custom Lua inputs, Custom Script bodies, and daemon loops.


Design principle: escape hatches

Node Meaning after import
Custom Lua Self-contained Cairo/HUD code. Studio does not turn it into Arc/Bar nodes and does not auto-rewrite its internal sensor logic onto graph wires.
Custom Script Self-contained shell (path and/or Inline script). May run as daemon and write its own cache; Custom Lua may read that cache directly.
Native sources / visuals Used when TEXT ${…} mapping is clear (CPU bar, hostname text, …).

Graph nodes you add later are extra layers. To feed them into Custom Lua, wire Input 1–12 and edit the Lua text to use in1in12.


Pipeline overview

theme folder (dialog lists files + hidden; Accept = folder, or parent of a selected file)
  ├── conf / conkyrc → Canvas + palette (colorN / font) + lua_load paths
  ├── TEXT section → cursor layout + text / bar / line / exec nodes
  ├── *.lua (lua_load) → Custom Lua visual node(s) + path rewrites
  └── scripts / images → Custom Script sources (path + optional script_body),
                          asset_paths on Custom Lua, daemon flags, warnings
        ↓
   Project (nodes + edges + warnings)
        ↓
   _layout_import_graph + _collapse_warnings
        ↓
   Studio canvas load → Build (same codegen as native) → user polish

Import produces a graph; Build writes start.sh / conf / Lua the same way as native projects.


UI entry (_import_legacy_theme)

  1. Folder dialog — non-native Qt dialog, files and hidden entries visible (ShowDirsOnly = false, QDir filters include Hidden). Selecting a file uses its parent directory as the theme root.
  2. import_legacy_theme(theme_dir) — parse + graph.
  3. load_project — open in Studio.
  4. builder.build_project into a sanitized name under the install root (spaces/parens from "name (imported)" are normalized).
  5. Failures are caught in stages (dialog / parse / canvas / build) with traceback dialogs instead of aborting the process.

Conf block → Canvas (+ palette)

Parsed from the Lua-style conky.config = { ... } (and classic settings where present):

Conf concept Studio target
minimum_width / height (and related; Lua locals like WIDTH resolved when possible) Canvas width / height
alignment, gap_x, gap_y Canvas placement
update_interval Influences FPS / timing defaults where mapped
default_color, color0color9 / colourN Import palette used by ${color…} in TEXT
font / default_font Default cursor font family / size for TEXT
lua_load Resolved relative to the theme dir; may be one path or a list
own_window_* Not always 1:1; Build applies session-aware window types later

lua_load resolution: relative paths, HOME concatenations, and sibling .lua files under the theme are collected. Multiple loads can become multiple Custom Lua nodes.


TEXT section → cursor + nodes

TEXT is walked line by line with a virtual cursor (x, y, font, colour) — the same mental model as Conky’s ${goto} / ${voffset} layout, not a full typesetting engine.

Cursor colour/font start from the conf palette and default font, not hard-coded white/Sans.

Layout directives

Directive Behaviour
${goto N} Set cursor x to N
${offset N} Add N to x
${voffset N} Add N to y
${alignc} / ${alignc N} Centre-ish x using canvas width (+ optional offset)
${alignr} / ${alignr N} Right-side x (canvas width − offset)
${font …} Update cursor font family / size
${color…} / ${colour…} Update cursor colour from palette index, #hex, or default
${hr} / ${stippled_hr} visual.hline at cursor; advances y slightly
blank lines Advance y by font size + spacing

Literal runs of text become visual.text nodes at the current cursor with the active font/colour. Placeholders like {{VALUE:id}} may be turned into String Format logic + a text visual when mixed with surrounding text.

Conditionals

${if_running…}, ${if_match…}, ${endif} are stripped. Content is kept; branching is not reconstructed. A warning is recorded when any conditional was removed.

Native variables → sources (deduplicated)

Identical type + props share one source node (get_or_create_source), so a TEXT-heavy theme does not spawn dozens of CPU boxes. Unknown registered types fall back to a Custom Script placeholder + warning instead of crashing the canvas.

Directive (examples) Studio source / visual
${cpu} / ${cpu N} source.cpu_percent (core mapped when possible)
${mem} / ${memperc} source.ram_percent
${fs_used_perc PATH} source.disk_percent
${fs_bar … PATH} Disk source + visual.bar
${battery} / ${battery_percent} source.battery_percent
${downspeed} / ${downspeedf} source.net_down
${upspeed} / ${upspeedf} source.net_up
${uptime} / ${uptime_short} source.uptime
${nodename} source.hostname
${kernel} source.kernel
${processes} source.process_count
${time FORMAT} source.datetime
${cpubar} / ${membar} Matching source + visual.bar
${cpugraph} / ${memgraph} Matching source + visual.history_graph

Unknown or exotic directives (${top}, swap bars, etc.) are counted and appear in one summary warning (or a short placeholder text node), not one warning per occurrence.


${execi} / ${exec} / friends

Form Import rule
${execi INTERVAL CMD} Interval + command extracted
${execpi} / ${texeci} Same family as execi
${exec CMD} Treated as rare/once-style; Custom Script with a long interval + warning
${execbar … CMD} Source (family/native/custom) + visual.bar

Classification of CMD (_classify_known_script):

  1. Native — known patterns → get_or_create_source for a built-in type.
  2. Family — known script families (weather, playerctl, …) → family source with poll_mode: execi and interval.
  3. ElseCustom Script: script_path set; small file contents copied into script_body (Inline script) so Properties can edit without an external editor.

TEXT may embed a {{VALUE:id}} placeholder so a following text visual can be wired. Unknown scripts are never dropped silently.


${image}

Flag / args Result
path, -p x,y, -s W×H Position/size parsed when present
-n (no cache) Often mapped to Album Art-style node + warning with original path
normal image visual.image_icon if a matching file is found under the theme tree
missing file Node still created with empty/partial path + warning to re-attach

Pure-Lua themes: images are usually drawn inside Custom Lua. Import attaches asset_paths on that node; Build copies them into assets/ and images/ and rewrites Lua paths toward ASSETS_DIR / CACHE_DIR. Separate Image nodes are not auto-created for that case (they would be a second independent drawing layer). Use Input 1–12 only for numeric/text data you choose to wire.


Cairo / Lua → Custom Lua (escape hatch)

Scope boundary (by design): the importer does not decompile Cairo into Arc/Bar/Star nodes, and does not force Studio sources to drive the imported draw code.

Input Output
Bodies from lua_load One or more visual.custom_lua nodes
Surface create/destroy / require 'cairo' / conky_window size Stripped or rewritten toward Studio’s shared cr / W / H
Asset / cache paths inside Lua Rewritten toward THEME_DIR / ASSETS_DIR / CACHE_DIR when patterns match
Scripts only referenced from Lua Discovered and turned into Custom Script nodes when possible
Click regions in Lua Clickable markers / commands when patterns are recognized
Internal vitals (safe_number('${cpu}'), sensors.cache, …) Left as written — edit the Lua text to change behaviour

Optional data flow (you opt in)

Each Custom Lua node has bindable Input 1–12. Codegen injects:

local in1 =-- SRC['…'] when wired, else nil
--
local in12 =

before the user’s Cairo body. Unwired slots are nil (not 0) so code can do:

local cpu = tonumber(in1) or safe_number('${cpu}', 0)

Wire Studio sources or logic only when you intentionally edit the Lua to read those locals. Import does not auto-patch Batman/Skyrim-style HUDs onto the graph.

Heavy hand-written themes remain mostly Custom Lua after import — editable as a text escape hatch, with companion scripts as separate Custom Script nodes.


Scripts & caches (Custom Script)

  • Shell scripts in the theme tree are listed; paths are recorded on Custom Script nodes.
  • script_body (Inline script) is preloaded for files under a size limit so the Properties panel can edit them like Custom Lua code.
  • At Build, non-empty Inline script wins over Script path; body is written under scripts/ (shebang added if missing).
  • Self-caching scripts (CACHE_FILE, sensors.cache, weather.cache, …) run under their original basename with no stdout wrapper, so the cache name the Lua expects stays intact.
  • Cache directory lines in scripts are patched toward the theme’s .runtime-cache layout when recognized.
  • Companion .conf files next to scripts (e.g. sensors.conf, weather.conf) are copied into scripts/ when present.
  • Unwired daemon Custom Scripts still appear in start.sh poll loops: pure-Lua HUDs read their caches directly, so the graph may have no edge into those sources.

Post-pass: layout & warnings

Step Rule
_layout_import_graph Sources in a left column; logic beside them; visuals near draw x/y or cx/cy
_collapse_warnings Drop noisy “unrecognised” spam; dedupe identical lines; cap list length
Ignored TEXT directives One summary line listing ${name}×count
Escape-hatch note One summary line stating Custom Lua/Script keep their own logic

Edge-case overrides & honesty limits

Situation Rule
Unknown ${…} var Counted in summary warning; optional placeholder text for top*
Nested complex TEXT Approximate positions
Pure Cairo art Custom Lua only (+ optional Input 1–12 if you edit the code)
Absolute paths to another machine Survives as strings until path rewrite or manual edit
Pixel parity Target useful migration, then polish in Studio
“Why doesn’t wiring a Bar change the Batman HUD?” Because the HUD is the Custom Lua node; Bars are separate layers unless you wire Inputs and change the Lua

Approximations appear in ImportResult.warnings — read them after import.


After import

  1. Fix Canvas size/alignment if needed.
  2. Treat Custom Lua and Custom Script as the theme’s brain: edit their text for behaviour.
  3. Confirm Build produces daemon loops for sensors/weather-style scripts; check .runtime-cache after ./start.sh.
  4. Optionally wire Custom Lua Input 1–12 and edit the Lua to consume them.
  5. Re-attach Image nodes only if you want Studio features as an extra layer.
  6. Live Preview / Build & Install (same pipeline as native).

See also Legacy Import (user-facing summary) and Theme Architecture & Codegen Pipeline.

Clone this wiki locally