Show plugin ids as subtext in the plugin picker menus
Select-menu options gain an optional third field rendered under the label, filtered alongside it, and returned with the selection. The plugin picker uses it to show every plugin's id and act on the id the selection hands back, replacing the duplicate-name label suffix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
9cb3640c9f
commit
bc75b03114
+7
-10
@@ -22,21 +22,18 @@ esac
|
||||
|
||||
plugins=$(omarchy-plugin-list --json)
|
||||
|
||||
# Two plugins may share a name, so keep the id with the row and show it when the
|
||||
# name alone cannot identify the pick.
|
||||
# The id rides along as row subtext: it tells same-named plugins apart on
|
||||
# screen and comes back with the selection as the key to act on.
|
||||
rows=$(jq -r --arg icon "$PLUGIN_ICON" \
|
||||
". as \$plugins
|
||||
| ([.[] | select($filter)]) as \$rows
|
||||
| \$rows[]
|
||||
| .name as \$name
|
||||
| (if ([\$rows[] | select(.name == \$name)] | length) > 1 then \$name + \" (\" + .id + \")\" else \$name end) as \$label
|
||||
| \$icon + \"\\t\" + \$label + \"\\t\" + .id" <<<"$plugins")
|
||||
| .[] | select($filter)
|
||||
| \$icon + \"\\t\" + .name + \"\\t\" + .id" <<<"$plugins")
|
||||
[[ -n $rows ]] || { omarchy-notification-send "No plugin to ${1}"; exit 0; }
|
||||
|
||||
name=$(omarchy-menu-select "${1^} plugin" < <(cut -f1,2 <<<"$rows")) || exit 0
|
||||
[[ -n $name ]] || exit 0
|
||||
selection=$(omarchy-menu-select "${1^} plugin" <<<"$rows") || exit 0
|
||||
[[ -n $selection ]] || exit 0
|
||||
|
||||
id=$(awk -F'\t' -v label="$name" '$2 == label { print $3; exit }' <<<"$rows")
|
||||
id=$(cut -f2 <<<"$selection")
|
||||
[[ -n $id ]] || exit 1
|
||||
|
||||
if [[ $1 == "clone" ]]; then
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
# omarchy:args=prompt [option...] [-- menu args...]
|
||||
# omarchy:examples=omarchy menu select Format jpg png|omarchy-menu-select Resolution 4k 1080p 720p -- --width 400
|
||||
|
||||
# An option may lead with an icon, as "<glyph><TAB><label>". The menu shows the
|
||||
# glyph and returns the label alone, so callers never strip it back off.
|
||||
# An option may lead with an icon, as "<glyph><TAB><label>", and may trail a
|
||||
# subtext shown under the label, as "<glyph><TAB><label><TAB><subtext>". The
|
||||
# menu shows the glyph but never returns it. A plain option returns the label
|
||||
# alone; an option with a subtext returns "<label><TAB><subtext>", so callers
|
||||
# with same-named rows get the subtext back as the stable key.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
+16
-10
@@ -141,8 +141,10 @@ Item {
|
||||
Util.execDetached(command)
|
||||
}
|
||||
|
||||
// Menu rows only surface their detail while a search is narrowing them;
|
||||
// dmenu rows carry caller-supplied subtext that must always be visible.
|
||||
function rowHeightForDetail(detail) {
|
||||
return root.filterText && detail ? root.detailRowHeight : root.baseRowHeight
|
||||
return (root.filterText || root.dmenuActive) && detail ? root.detailRowHeight : root.baseRowHeight
|
||||
}
|
||||
|
||||
// Height the card can devote to rows before running off the screen — or
|
||||
@@ -206,7 +208,7 @@ Item {
|
||||
var total = 0
|
||||
for (var i = 0; i < displayModel.count; i++) {
|
||||
if (i > 0) total += root.rowSpacing
|
||||
total += root.baseRowHeight
|
||||
total += root.rowHeightForDetail(displayModel.get(i).detail)
|
||||
totals.push(total)
|
||||
}
|
||||
|
||||
@@ -516,13 +518,16 @@ Item {
|
||||
|
||||
var query = root.filterText.trim().toLowerCase()
|
||||
for (var i = 0; i < root.dmenuOptions.length; i++) {
|
||||
// An option may lead with an icon, as "<glyph>\t<label>". Only the label
|
||||
// is filtered against and handed back, so the caller never sees a glyph
|
||||
// it has to strip off the selection.
|
||||
// An option is "<label>", "<glyph>\t<label>", or
|
||||
// "<glyph>\t<label>\t<subtext>". The glyph never comes back with the
|
||||
// selection; the subtext renders under the label, filters alongside it,
|
||||
// and returns with the selection as a stable key for same-named rows.
|
||||
var parts = String(root.dmenuOptions[i] || "").split("\t")
|
||||
var icon = parts.length > 1 ? parts.shift() : ""
|
||||
var label = parts.join("\t")
|
||||
if (query && label.toLowerCase().indexOf(query) < 0) continue
|
||||
var label = parts.shift() || ""
|
||||
var detail = parts.join("\t")
|
||||
if (query && label.toLowerCase().indexOf(query) < 0
|
||||
&& detail.toLowerCase().indexOf(query) < 0) continue
|
||||
displayModel.append({
|
||||
itemId: "dmenu." + i,
|
||||
kind: "dmenu",
|
||||
@@ -532,7 +537,7 @@ Item {
|
||||
appId: "",
|
||||
label: label,
|
||||
target: "",
|
||||
detail: "",
|
||||
detail: detail,
|
||||
path: "",
|
||||
childCount: 0,
|
||||
action: "",
|
||||
@@ -717,7 +722,8 @@ Item {
|
||||
return
|
||||
}
|
||||
if (index < 0 || index >= displayModel.count) return
|
||||
root.applyDmenuSelection(displayModel.get(index).label)
|
||||
var picked = displayModel.get(index)
|
||||
root.applyDmenuSelection(picked.detail ? picked.label + "\t" + picked.detail : picked.label)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1264,7 +1270,7 @@ Item {
|
||||
Text {
|
||||
width: parent.width
|
||||
text: row.detail
|
||||
visible: root.filterText && row.detail.length > 0
|
||||
visible: (root.filterText || row.kind === "dmenu") && row.detail.length > 0
|
||||
color: root.foreground
|
||||
opacity: 0.52
|
||||
font.family: root.fontFamily
|
||||
|
||||
@@ -65,8 +65,8 @@ pick() {
|
||||
CALLS=$(cat "$TMPDIR/calls")
|
||||
}
|
||||
|
||||
# Two plugins can declare the same display name. When both are eligible for the
|
||||
# same verb, neither row can stand on the name alone.
|
||||
# Two plugins can declare the same display name. The id rides along as row
|
||||
# subtext and comes back with the selection, so the pick resolves by id.
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": true},
|
||||
@@ -74,46 +74,26 @@ cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Clock (tester.clock)"
|
||||
[[ $ROWS == *"Clock (omarchy.clock)"* && $ROWS == *"Clock (tester.clock)"* ]] \
|
||||
|| fail "picker tells two plugins of the same name apart" "$ROWS"
|
||||
pass "picker tells two plugins of the same name apart"
|
||||
pick enable "$(printf 'Clock\ttester.clock')"
|
||||
[[ $ROWS == *"$(printf 'Clock\tomarchy.clock')"* && $ROWS == *"$(printf 'Clock\ttester.clock')"* ]] \
|
||||
|| fail "picker offers the id as subtext on every row" "$ROWS"
|
||||
pass "picker offers the id as subtext on every row"
|
||||
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|
||||
|| fail "picker acts on the row that was picked, not the one that shares its name" "$CALLS"
|
||||
pass "picker acts on the row that was picked, not the one that shares its name"
|
||||
|
||||
# Only one of them is eligible, so the row needs no id -- but resolving it by
|
||||
# name alone would still find the wrong plugin, since the other one exists.
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
|
||||
{"id": "tester.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Clock"
|
||||
[[ $ROWS != *"("* ]] || fail "picker adorns a row only when its name is taken twice over" "$ROWS"
|
||||
pass "picker adorns a row only when its name is taken twice over"
|
||||
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|
||||
|| fail "picker resolves a lone row to the plugin the verb offered, not a namesake it filtered out" "$CALLS"
|
||||
pass "picker resolves a lone row to the plugin the verb offered, not a namesake it filtered out"
|
||||
|
||||
pick remove "Clock"
|
||||
pick remove "$(printf 'Clock\ttester.clock')"
|
||||
[[ $CALLS == *"omarchy-plugin-remove tester.clock"* ]] \
|
||||
|| fail "picker removes the plugin whose row was picked" "$CALLS"
|
||||
pass "picker removes the plugin whose row was picked"
|
||||
|
||||
# A name that stands alone is left alone: no id trailing a row that needs none.
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "acme.weather", "name": "Weather", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Weather"
|
||||
[[ $ROWS == *"Weather"* && $ROWS != *"acme.weather)"* ]] \
|
||||
|| fail "picker leaves an unambiguous name unadorned" "$ROWS"
|
||||
pass "picker leaves an unambiguous name unadorned"
|
||||
pick enable "$(printf 'Weather\tacme.weather')"
|
||||
[[ $CALLS == *"omarchy-plugin-enable acme.weather"* ]] \
|
||||
|| fail "picker delegates plugin enablement to the plugin command" "$CALLS"
|
||||
pass "picker delegates plugin enablement to the plugin command"
|
||||
@@ -127,7 +107,7 @@ cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
]
|
||||
JSON
|
||||
|
||||
pick clone "Clock"
|
||||
pick clone "$(printf 'Clock\tomarchy.clock')"
|
||||
[[ $ROWS == *"Clock"* && $ROWS != *"Weather"* ]] ||
|
||||
fail "clone picker offers only built-in plugins" "$ROWS"
|
||||
pass "clone picker offers built-in plugins"
|
||||
@@ -157,7 +137,7 @@ cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Fancy"
|
||||
pick enable "$(printf 'Fancy\tacme.fancy')"
|
||||
[[ $CALLS == *"omarchy-plugin-enable acme.fancy"* && $CALLS != *"--section"* ]] \
|
||||
|| fail "picker delegates kind-specific enablement" "$CALLS"
|
||||
pass "picker delegates kind-specific enablement"
|
||||
@@ -171,7 +151,7 @@ cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
]
|
||||
JSON
|
||||
|
||||
pick disable "Clock"
|
||||
pick disable "$(printf 'Clock\tomarchy.clock')"
|
||||
[[ $ROWS == *"Clock"* && $ROWS != *"Fancy"* ]] \
|
||||
|| fail "picker keeps a bar out of disable" "$ROWS"
|
||||
pass "picker keeps a bar out of disable"
|
||||
@@ -184,7 +164,7 @@ cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Bar"
|
||||
pick enable "$(printf 'Bar\tomarchy.bar')"
|
||||
[[ $ROWS == *"Bar"* && $ROWS != *"Neon Bar"* ]] \
|
||||
|| fail "picker offers every bar except the one already running" "$ROWS"
|
||||
pass "picker offers every bar except the one already running"
|
||||
|
||||
@@ -291,18 +291,18 @@ assert(
|
||||
/"omarchy-plugin-\$1" "\$id"/.test(pluginPicker),
|
||||
'plugin picker delegates enable and disable without interpreting plugin kinds'
|
||||
)
|
||||
// Icons ride along as "<glyph>\tlabel"; the menu shows the glyph and hands
|
||||
// back the label, so nothing downstream has to strip one off. The id rides in
|
||||
// a third field, cut off before the menu is ever shown it. What the picker
|
||||
// Icons ride along as "<glyph>\tlabel\tsubtext"; the menu shows the glyph,
|
||||
// renders the subtext under the label, and hands back "label\tsubtext" so the
|
||||
// picker can act on the id without resolving a display name. What the picker
|
||||
// then does with the row it gets back is checked in menu-plugin-test.sh.
|
||||
assert(
|
||||
/\$label \+ \\"\\\\t\\" \+ \.id/.test(pluginPicker)
|
||||
&& /omarchy-menu-select "\$\{1\^\} plugin" < <\(cut -f1,2 <<<"\$rows"\)/.test(pluginPicker),
|
||||
'plugin picker labels its rows with glyphs and keeps the id out of the label'
|
||||
/\.name \+ \\"\\\\t\\" \+ \.id/.test(pluginPicker)
|
||||
&& /id=\$\(cut -f2 <<<"\$selection"\)/.test(pluginPicker),
|
||||
'plugin picker shows the id as row subtext and acts on the id the selection hands back'
|
||||
)
|
||||
assert(
|
||||
/var icon = parts\.length > 1 \? parts\.shift\(\) : ""\s*\n\s*var label = parts\.join\("\\t"\)/.test(menuQml),
|
||||
'menu select mode reads a leading icon off an option and filters on the label alone'
|
||||
/var icon = parts\.length > 1 \? parts\.shift\(\) : ""\s*\n\s*var label = parts\.shift\(\) \|\| ""\s*\n\s*var detail = parts\.join\("\\t"\)/.test(menuQml),
|
||||
'menu select mode reads a leading icon and a trailing subtext off an option'
|
||||
)
|
||||
assert(
|
||||
/omarchy-launch-floating-terminal-with-presentation "omarchy-plugin-remove/.test(pluginPicker),
|
||||
|
||||
Reference in New Issue
Block a user