A center-anchored module is mounted twice: the copy that is drawn, and a zero-size placeholder holding its place in the flow beside the anchor. findPanelWidget returned whichever registered first, and that order is not stable across a live bar reconfiguration, so a panel could open anchored to the invisible copy -- mispositioned, with the drawn slot never lighting up and switchPanelFrom unable to find it again. The mark was also always 55% of the slot, which fits an icon but underlines only a fraction of a text label, and runs the full height of a multi-line module on a vertical bar. Modules can now say how long the mark should be along the bar; anything that does not answer keeps the old proportion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
139 lines
4.0 KiB
JavaScript
139 lines
4.0 KiB
JavaScript
function isPlainObject(value) {
|
|
return !!value && typeof value === "object" && !Array.isArray(value)
|
|
}
|
|
|
|
function normalizePosition(value) {
|
|
var next = String(value || "").trim()
|
|
return /^(top|bottom|left|right)$/.test(next) ? next : "top"
|
|
}
|
|
|
|
function entrySettings(entry) {
|
|
if (!isPlainObject(entry)) return {}
|
|
var copy = {}
|
|
for (var key in entry) {
|
|
if (key === "id") continue
|
|
copy[key] = entry[key]
|
|
}
|
|
return copy
|
|
}
|
|
|
|
function entryId(entry) {
|
|
if (typeof entry === "string") return entry
|
|
if (isPlainObject(entry)) {
|
|
var id = entry["id"]
|
|
if (id !== undefined && id !== null && String(id) !== "") return String(id)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
function pinTrayToInner(entries, section) {
|
|
var trayEntry = null
|
|
var result = []
|
|
var values = Array.isArray(entries) ? entries : []
|
|
for (var i = 0; i < values.length; i++) {
|
|
if (entryId(values[i]) === "omarchy.tray") trayEntry = values[i]
|
|
else result.push(values[i])
|
|
}
|
|
if (trayEntry) {
|
|
if (section === "right") result.unshift(trayEntry)
|
|
else result.push(trayEntry)
|
|
}
|
|
return result
|
|
}
|
|
|
|
function moduleString(entry, key, fallback) {
|
|
var settings = entrySettings(entry)
|
|
var value = settings[key]
|
|
return value === undefined || value === null ? fallback : String(value)
|
|
}
|
|
|
|
function entryIndex(entries, name) {
|
|
if (!Array.isArray(entries)) return -1
|
|
for (var i = 0; i < entries.length; i++) {
|
|
if (entryId(entries[i]) === name) return i
|
|
}
|
|
return -1
|
|
}
|
|
|
|
function entriesBefore(entries, name) {
|
|
var index = entryIndex(entries, name)
|
|
return index <= 0 ? [] : entries.slice(0, index)
|
|
}
|
|
|
|
function entriesAfter(entries, name) {
|
|
var index = entryIndex(entries, name)
|
|
return index === -1 ? [] : entries.slice(index + 1)
|
|
}
|
|
|
|
function expandPath(value, home) {
|
|
var path = String(value || "")
|
|
if (path === "") return ""
|
|
if (path.indexOf("~/") === 0) return home + path.substring(1)
|
|
if (path.indexOf("$HOME/") === 0) return home + path.substring(5)
|
|
return path
|
|
}
|
|
|
|
function customModuleSafeName(name) {
|
|
var value = String(name || "")
|
|
return value !== "" && value.indexOf("..") === -1 && value[0] !== "/"
|
|
}
|
|
|
|
function customModuleType(entry) {
|
|
var settings = entrySettings(entry)
|
|
var type = String(settings.type || "")
|
|
if (type) return type
|
|
if (settings.exec) return "command"
|
|
if (settings.source) return "qml"
|
|
return ""
|
|
}
|
|
|
|
function customModulePath(entry, home, configDir) {
|
|
var settings = entrySettings(entry)
|
|
var name = entryId(entry)
|
|
var source = settings.source ? expandPath(settings.source, home) : ""
|
|
if (!source && customModuleSafeName(name))
|
|
source = String(configDir || "") + "/bar/modules/" + String(name) + ".qml"
|
|
return source
|
|
}
|
|
|
|
// A center module is mounted twice once an anchor is set: the copy that is
|
|
// actually drawn, and a zero-size placeholder holding its place in the flow
|
|
// beside the anchor. Panel routing has to pick the drawn one — it is the only
|
|
// one that can anchor a popup, carry the open-panel mark, or be found again
|
|
// by switchPanelFrom — and fall back to the placeholder only when nothing is
|
|
// on screen. The order the two are registered in is not stable across a live
|
|
// bar reconfiguration, so picking the first match is not good enough.
|
|
function isDrawnSlot(slot) {
|
|
return !!slot && slot.visible === true && slot.width > 0 && slot.height > 0
|
|
}
|
|
|
|
function pickDrawnSlot(slots) {
|
|
var placeholder = null
|
|
var list = slots || []
|
|
for (var i = 0; i < list.length; i++) {
|
|
if (!list[i]) continue
|
|
if (isDrawnSlot(list[i])) return list[i]
|
|
if (!placeholder) placeholder = list[i]
|
|
}
|
|
return placeholder
|
|
}
|
|
|
|
if (typeof module !== "undefined") {
|
|
module.exports = {
|
|
isDrawnSlot: isDrawnSlot,
|
|
pickDrawnSlot: pickDrawnSlot,
|
|
normalizePosition: normalizePosition,
|
|
entrySettings: entrySettings,
|
|
entryId: entryId,
|
|
pinTrayToInner: pinTrayToInner,
|
|
moduleString: moduleString,
|
|
entryIndex: entryIndex,
|
|
entriesBefore: entriesBefore,
|
|
entriesAfter: entriesAfter,
|
|
expandPath: expandPath,
|
|
customModuleSafeName: customModuleSafeName,
|
|
customModuleType: customModuleType,
|
|
customModulePath: customModulePath
|
|
}
|
|
}
|