L
lingcoder
↓ 0 downloadsLingCoder: a Geany / VS Code-style IDE for Ling, written in Ling. Explorer, tabs, syntax-coloured editor with lint, find in files, command palette, run/build panel, and hot-reloaded extensions - including a Claude assistant.
Keywords ide, editor, ling, claude, extensions
lingfu install lingcoderCode / Files (34)
extensions/miao/miao.ling
# miao.ling - the miao (temple of versions) panel for LingCoder: repo stats at a glance
# and every ceremony one click away, in the sidebar or as a full tab. Works in any
# folder that holds a .miao temple ("and other projects too"): the miao program itself
# is found next door in ../ling-source (override with miao_home= in settings.txt).
# The Claude extension asks this one where miao lives, so `miao ...` commands Claude
# runs are routed here too.
fn miao_info() { return ["Miao - temple of versions", "Version control for Ling projects: divine, offer, rite, lineage ... - never git", "1.0"] }
fn miao_panel_info() { return ["Miao - the temple of versions (repo panel)", "home"] }
fn miao_commands() {
return ["panel|Miao: Open Panel||home", "tab|Miao: Open as Tab||window", "divine|Miao: Divine (status)||eye", "offer_all|Miao: Offer All (stage .)||plus", "rite|Miao: Rite (commit)...||save", "lineage|Miao: Lineage (log)||bars", "yinyang|Miao: YinYang (diff)||layers", "pilgrimage|Miao: Pilgrimage (pull)||arrow_down", "tribute|Miao: Tribute (push)||arrow_up", "chronicle|Miao: Chronicle (reflog)||clock"]
}
# ── where things are ──────────────────────────────────────────────────────────
fn miao_home() {
bind s = str_trim(st_get("miao_home", ""))
if s != "" && file_exists(p_join(s, "ling/rites.ling")) { return p_norm(s) }
bind near = p_join(p_dir(app_root()), "ling-source")
if file_exists(p_join(near, "ling/rites.ling")) { return near }
return ""
}
# the temple root at or above the open folder ("" when none)
fn miao_repo_root() {
bind start = proj_root()
if start == "" && ide_cur_path() != "" { bind start = p_dir(ide_cur_path()) }
if start == "" { return "" }
if st_get("mi_root_for", "") == start && st_get("now", 0.0) - st_get("mi_root_t", 0.0 - 10.0) < 3.0 { return st_get("mi_root", "") }
bind d = p_norm(start)
bind found = ""
bind k = 0.0
while k < 8.0 && d != "" && found == "" {
if is_true(fs_is_dir(p_join(d, ".miao"))) { bind found = d }
bind up = fs_parent(d)
if p_norm(up) == d { bind k = 8.0 }
bind d = p_norm(up)
bind k = k + 1.0
}
st_set("mi_root_for", start)
st_set("mi_root", found)
st_set("mi_root_t", st_get("now", 0.0))
return found
}
fn miao_dir() { return p_join(miao_repo_root(), ".miao") }
fn miao_run(id) {
if id == "panel" { side_show("ext:miao") return "" }
if id == "tab" { ide_open_panel_tab("miao", "Miao Temple", "home") return "" }
if id == "divine" { miao_exec(["divine"], "divine") return "" }
if id == "offer_all" { miao_exec(["offer", "."], "offer .") return "" }
if id == "rite" {
side_show("ext:miao")
st_set("focus", "mi_msg")
st_set("text_owner", "")
return "Type the rite message in the Miao panel, then press Rite"
}
if id == "lineage" { miao_exec(["lineage", "-n", "15"], "lineage") return "" }
if id == "yinyang" { miao_exec(["yinyang"], "yinyang") return "" }
if id == "pilgrimage" { miao_exec(["pilgrimage"], "pilgrimage") return "" }
if id == "tribute" { miao_exec(["tribute"], "tribute") return "" }
if id == "chronicle" { miao_exec(["chronicle"], "chronicle") return "" }
return ""
}
# ── running ceremonies ────────────────────────────────────────────────────────
fn miao_busy() { return st_get("mi_job", 0.0 - 1.0) >= 0.0 }
fn miao_log() { return p_join(cache_dir(), "miao.log") }
fn miao_exec(args, label) {
if miao_busy() { ide_status("A miao ceremony is already running") return 0.0 }
bind home = miao_home()
if home == "" { ide_status("miao was not found - set miao_home= in settings.txt") return 0.0 }
bind argv = ["run", "--interp", p_join(home, "ling/main.ling")]
bind i = 0.0
while i < len(args) {
bind argv = list_push(argv, list_get(args, i))
bind i = i + 1.0
}
bind argv = list_push(argv, "--plain")
bind dir = miao_repo_root()
if dir == "" { bind dir = sel(proj_root() == "", app_root(), proj_root()) }
bind h = plat_job_start(ling_exe_path(), argv, dir, miao_log())
if h < 0.0 { ide_status("Could not start miao") return 0.0 }
st_set("mi_job", h)
st_set("mi_label", label)
st_set("mi_out", "$ miao " + label + "\n")
st_set("mi_t0", st_get("now", 0.0))
st_set("ext_busy_miao", 1.0)
st_set("mi_off", 0.0 - 1.0)
ide_status("miao " + label + " ...")
return 0.0
}
fn miao_tick() {
if !miao_busy() { return 0.0 }
if st_get("now", 0.0) - st_get("mi_poll", 0.0) < 0.3 { return 0.0 }
st_set("mi_poll", st_get("now", 0.0))
bind t = plat_job_log(miao_log())
st_set("mi_out", "$ miao " + st_get("mi_label", "") + "\n" + t)
bind code = plat_job_poll(st_get("mi_job", 0.0 - 1.0), miao_log())
if code > 0.0 - 1.5 {
st_set("mi_job", 0.0 - 1.0)
st_set("ext_busy_miao", 0.0)
st_set("mi_stat_t", 0.0 - 10.0)
dir_invalidate(miao_repo_root())
ide_status("miao " + st_get("mi_label", "") + sel(code == 0.0, " done", " failed (exit " + num_str(code) + ")"))
}
return 0.0
}
# ── temple stats straight from .miao (cached 2 s) ─────────────────────────────
fn miao_stats() {
if st_get("now", 0.0) - st_get("mi_stat_t", 0.0 - 10.0) < 2.0 { return 0.0 }
st_set("mi_stat_t", st_get("now", 0.0))
bind d = miao_dir()
bind soul = str_trim(str_replace(plat_read(p_join(d, "SOUL")), "soul:", ""))
st_set("mi_soul", sel(soul == "", "main", soul))
bind items = fs_list(p_join(d, "souls"))
bind names = ""
bind i = 0.0
while i < len(items) / 3.0 {
bind names = names + sel(names == "", "", ", ") + list_get(items, i * 3.0)
bind i = i + 1.0
}
st_set("mi_souls", names)
st_set("mi_nsouls", len(items) / 3.0)
st_set("mi_ntal", len(fs_list(p_join(d, "talismans"))) / 3.0)
bind cfg = plat_read(p_join(d, "config"))
st_set("mi_keeper", str_trim(cfg_get(str_replace(cfg, "|", "="), "author", "(unnamed)")))
bind altar = str_trim(plat_read(p_join(d, "altar")))
bind staged = 0.0
if altar != "" { bind staged = len(str_split(altar, "\n")) }
st_set("mi_staged", staged)
bind head = str_trim(plat_read(p_join(d, "souls/" + st_get("mi_soul", "main"))))
st_set("mi_head", sel(head == "", "(no rites yet)", str_slice(head, 0.0, 8.0)))
bind chron = str_trim(plat_read(p_join(d, "chronicle")))
bind nrites = 0.0
bind last = ""
if chron != "" {
bind lines = str_split(chron, "\n")
bind k = 0.0
while k < len(lines) {
bind parts = str_split(list_get(lines, k), "|")
if len(parts) > 1.5 && list_get(parts, 1) == "rite" { bind nrites = nrites + 1.0 }
bind k = k + 1.0
}
bind lparts = str_split(list_get(lines, len(lines) - 1.0), "|")
if len(lparts) > 1.5 { bind last = list_get(lparts, 1) }
}
st_set("mi_nrites", nrites)
st_set("mi_last", last)
return 0.0
}
fn miao_kv(x, y, w, k, v) {
g_text(x, y, 11.0, C_TEXT_DIM, k)
g_text_r(x + w, y, 11.0, C_TEXT_STRONG, g_fit(11.0, w * 0.62, v))
return 0.0
}
fn miao_btn(id, x, y, w, label, alias, col) {
bind r = u_button(id, x, y, w, 22.0, label, col)
if u_hot(x, y, w, 22.0) { u_tip("miao " + str_lower(label) + " (git: " + alias + ")") }
return r
}
# ── the panel ─────────────────────────────────────────────────────────────────
fn miao_panel(px, py, pw, ph) {
bind wide = pw > 700.0
bind x = px
bind w = sel(wide, 280.0, pw)
ds_word(x + 10.0, py + 10.0, 10.0, "MIAO", ds_col("asm"), false)
g_text(x + 64.0, py + 11.0, 10.0, C_TEXT_DIM, "庙 - temple of versions")
if st_get("ptab_active", 0.0) < 0.5 || st_get("ptab_stem", "") != "miao" {
if u_icon_button("mi_totab", x + w - 26.0, py + 4.0, 20.0, 18.0, "window", 10.0, C_TEXT_DIM, false) { miao_run("tab") }
if u_hot(x + w - 26.0, py + 4.0, 20.0, 18.0) { u_tip("Open the temple as a full tab") }
}
bind y = py + 30.0
if miao_home() == "" {
g_text_wrap(x + 12.0, y + 6.0, w - 24.0, 11.0, ds_col("bind"), "The miao program was not found. It usually lives in ../ling-source; set miao_home=<folder> in settings.txt if yours is elsewhere.")
return 0.0
}
bind root = miao_repo_root()
if root == "" {
g_text_wrap(x + 12.0, y + 6.0, w - 24.0, 11.0, C_TEXT_DIM, "No temple stands here" + sel(proj_root() == "", " - open a folder first (Ctrl+Shift+O).", "."))
if proj_root() != "" {
if u_button("mi_consecrate", x + 12.0, y + 54.0, w - 24.0, 26.0, "Consecrate this folder (init)", C_SEL) { miao_exec(["consecrate", proj_root()], "consecrate") }
}
miao_out_view(sel(wide, px + w + 8.0, x), sel(wide, py + 4.0, y + 92.0), sel(wide, pw - w - 16.0, w - 8.0), sel(wide, ph - 8.0, ph - (y + 92.0 - py) - 4.0))
return 0.0
}
miao_stats()
# stats
miao_kv(x + 12.0, y, w - 24.0, "temple", p_base(root))
miao_kv(x + 12.0, y + 16.0, w - 24.0, "soul (branch)", st_get("mi_soul", "main") + sel(st_get("mi_nsouls", 1.0) > 1.0, " (of " + num_str(st_get("mi_nsouls", 1.0)) + ")", ""))
miao_kv(x + 12.0, y + 32.0, w - 24.0, "head rite", st_get("mi_head", ""))
miao_kv(x + 12.0, y + 48.0, w - 24.0, "rites / talismans", num_str(st_get("mi_nrites", 0.0)) + " / " + num_str(st_get("mi_ntal", 0.0)))
miao_kv(x + 12.0, y + 64.0, w - 24.0, "on the altar", num_str(st_get("mi_staged", 0.0)) + " offering(s)")
miao_kv(x + 12.0, y + 80.0, w - 24.0, "keeper", st_get("mi_keeper", ""))
bind y = y + 100.0
u_hsep(x + 8.0, y, w - 16.0)
bind y = y + 8.0
# everyday ceremonies
bind bw = floor((w - 28.0) / 2.0)
if miao_btn("mi_divine", x + 10.0, y, bw, "Divine", "status", C_SEL) { miao_exec(["divine"], "divine") }
if miao_btn("mi_offer", x + 14.0 + bw, y, bw, "Offer all", "add .", 0.0) { miao_exec(["offer", "."], "offer .") }
bind y = y + 26.0
if miao_btn("mi_lineage", x + 10.0, y, bw, "Lineage", "log", 0.0) { miao_exec(["lineage", "-n", "15"], "lineage") }
if miao_btn("mi_yy", x + 14.0 + bw, y, bw, "YinYang", "diff", 0.0) { miao_exec(["yinyang"], "yinyang") }
bind y = y + 26.0
if miao_btn("mi_pil", x + 10.0, y, bw, "Pilgrimage", "pull", 0.0) { miao_exec(["pilgrimage"], "pilgrimage") }
if miao_btn("mi_tri", x + 14.0 + bw, y, bw, "Tribute", "push", 0.0) { miao_exec(["tribute"], "tribute") }
bind y = y + 26.0
if miao_btn("mi_chron", x + 10.0, y, bw, "Chronicle", "reflog", 0.0) { miao_exec(["chronicle"], "chronicle") }
if miao_btn("mi_fair", x + 14.0 + bw, y, bw, "Fair", "serve", 0.0) { miao_exec(["fair"], "fair") }
bind y = y + 30.0
# the rite (commit)
g_text(x + 10.0, y + 1.0, 10.0, C_TEXT_DIM, "RITE (commit) - the message:")
bind y = y + 14.0
bind msg = u_textfield("mi_msg", x + 10.0, y, w - 86.0, 22.0, st_get("mi_msg", ""))
st_set("mi_msg", msg)
bind fire = u_button("mi_rite", x + w - 70.0, y, 60.0, 22.0, "Rite", C_SEL)
if st_get("focus", "") == "mi_msg" && key_pressed("enter") && st_get("modal", "") == "" { bind fire = true }
if fire && str_trim(msg) != "" {
miao_exec(["rite", "-m", str_trim(msg)], "rite")
st_set("mi_msg", "")
text_set("")
}
bind y = y + 30.0
# ceremonies that take a name / path
g_text(x + 10.0, y + 1.0, 10.0, C_TEXT_DIM, "WITH THE ARGUMENT BELOW:")
bind y = y + 14.0
bind arg = u_textfield("mi_arg", x + 10.0, y, w - 20.0, 22.0, st_get("mi_arg", ""))
st_set("mi_arg", arg)
bind y = y + 26.0
bind tw = floor((w - 32.0) / 3.0)
bind a = str_trim(arg)
if miao_btn("mi_summon", x + 10.0, y, tw, "Summon", "switch", 0.0) { if a != "" { miao_exec(["summon", a], "summon " + a) } }
if miao_btn("mi_soul", x + 14.0 + tw, y, tw, "Soul", "branch", 0.0) { miao_exec(sel(a == "", ["soul"], ["soul", a]), "soul") }
if miao_btn("mi_harm", x + 18.0 + tw * 2.0, y, tw, "Harmonize", "merge", 0.0) { if a != "" { miao_exec(["harmonize", a], "harmonize " + a) } }
bind y = y + 26.0
if miao_btn("mi_tal", x + 10.0, y, tw, "Talisman", "tag", 0.0) { miao_exec(sel(a == "", ["talisman"], ["talisman", a]), "talisman") }
if miao_btn("mi_mani", x + 14.0 + tw, y, tw, "Manifest", "show", 0.0) { miao_exec(sel(a == "", ["manifest"], ["manifest", a]), "manifest") }
if miao_btn("mi_rest", x + 18.0 + tw * 2.0, y, tw, "Restore", "restore", 0.0) { if a != "" { miao_exec(["restore", a], "restore " + a) } }
bind y = y + 26.0
if miao_btn("mi_withdraw", x + 10.0, y, tw, "Withdraw", "unstage", 0.0) { if a != "" { miao_exec(["withdraw", a], "withdraw " + a) } }
if miao_btn("mi_mother", x + 14.0 + tw, y, tw, "Mother", "remote", 0.0) { miao_exec(sel(a == "", ["mother"], ["mother", a]), "mother") }
if miao_btn("mi_keeperb", x + 18.0 + tw * 2.0, y, tw, "Keeper", "config", 0.0) { miao_exec(sel(a == "", ["keeper"], ["keeper", a]), "keeper") }
bind y = y + 32.0
# output
if wide {
miao_out_view(px + w + 8.0, py + 4.0, pw - w - 16.0, ph - 8.0)
} else {
miao_out_view(x + 4.0, y, w - 8.0, ph - (y - py) - 6.0)
}
return 0.0
}
fn miao_out_view(x, y, w, h) {
if h < 40.0 { return 0.0 }
g_rect(x, y, w, h, E_BG)
px_outline(x, y, w, h, C_LINE_SOFT, 0.8)
bind t = st_get("mi_out", "")
if t == "" {
g_text(x + 10.0, y + 8.0, 11.0, C_TEXT_DIM, "Ceremony output appears here.")
return 0.0
}
bind lines = str_split(t, "\n")
bind n = len(lines)
bind lh = 15.0
bind maxoff = max(0.0, n * lh - h + 10.0)
bind off = st_get("mi_off", 0.0 - 1.0)
if off < 0.0 || off > maxoff - lh { bind off = maxoff }
bind noff = u_scroll_y("mi_osb", x, y, w, h, n * lh + 10.0, off)
if noff != off { bind off = noff }
if off >= maxoff - 1.0 { st_set("mi_off", 0.0 - 1.0) } else { st_set("mi_off", off) }
u_clip(x + 2.0, y + 2.0, w - 4.0, h - 4.0)
bind i = floor(off / lh)
bind last = min(n, i + ceil(h / lh) + 1.0)
while i < last {
bind ln = list_get(lines, i)
bind col = E_TEXT
if str_starts_with(ln, "$") { bind col = E_INFO }
else if str_find(str_lower(ln), "error") >= 0.0 || str_find(ln, "✗") >= 0.0 { bind col = E_ERROR }
else if str_find(ln, "✦") >= 0.0 || str_find(ln, "+") == 0.0 { bind col = E_STRING }
else if str_find(ln, "-") == 0.0 { bind col = E_FUNC }
g_text_fit(x + 8.0, y + 6.0 + i * lh - off, w - 24.0, 11.0, col, ln)
bind i = i + 1.0
}
u_unclip()
if miao_busy() {
bind sp = floor(st_get("now", 0.0) * 8.0) % 4.0
g_text_r(x + w - 14.0, y + 6.0, 11.0, ds_col("ctl"), st_get("mi_label", "") + str_repeat(".", sp + 1.0))
}
return 0.0
}