BTT as Übersicht/GeekTool replacement?

Hello, I would like to replace Übersicht with the new BTT's floating menus but I struggle to create event the simplest "widget". Is it event possible to use it like that? Is it meant to be used like that?

Simple widget description:
I am psn trophy hunter and I have Übersicht widget that shows my trophy card on my desktop. The trophy card is publicly available .png image. Let's pretend, that this is the link to the trophy card: https://upload.wikimedia.org/wikipedia/commons/8/89/PNG-Gradient.png. How/Can I use BTT to show this image on my desktop aligned to bottom right corner? Maybe even better for that would be macOS native desktop widgets feature.

The Übersicht code is:

// PSN Card Image Widget for Übersicht

// Image URL
const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/8/89/PNG-Gradient.png";

// Refresh every X miliseconds
export const refreshFrequency = 1800000;

// Base layout
export const className = {
    opacity: 0.5,
    bottom: '5px',
    right: '5px',
};

// Render the widget
export const render = ({ output, error }) => {
    return (
        <img src={imageUrl} style={{ width: 'auto', height: '100%' }} />
    );
};

This is currently my best shot.

wip.bttpreset (13.1 KB)

Check this one - exported_trigger_trophy.bttpreset (25.2 KB)

Yep currently a web view item is the best way (you can use chatgpt to convert your übersicht widget to plain html + js). in general it should also be possible with standard items and some java script, but I'm currently ironing out some final issues there. I'll post an update here once this stuff is also fully possible without webview items.

In 4.900 alpha I added a fetchURLAsBase64 function that makes it easier to do this without a Webview item as well:

async function itemScript(itemUUID) {
    let imageBase64 = await fetchURLAsBase64("https://upload.wikimedia.org/wikipedia/commons/8/89/PNG-Gradient.png");

	let content = 	{
	    "BTTMenuItemIconType": 1,
        "BTTMenuItemIconPosition": 4,
	    "BTTMenuItemImage": imageBase64
	 };

 	return JSON.stringify(content);
}


1 Like

Thx for your example. I was able to use it as base and achieve the 1:1 result from what I have in XXX . It's shame that to achieve this result, I need to hardcode many things (size of image + padding). Is there way to reload the image let's say every 6 hours? I see that there is Script tab there but I don't know how would I achieve that.

image

I'll check the alpha version and report back with results. Thx for that.

I was just thinking about how would be possible to "convert" the scripts that are using bash scripts: uebersicht-ip/ip.widget/ip.jsx at master · bbredewold/uebersicht-ip · GitHub

Is that possible or lost cause for now?

This example HTML would refresh every 30 min:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PSN Card Image Widget</title>
    
</head>
<body>
    <div id="widget">
        <img id="psn-card" src="https://upload.wikimedia.org/wikipedia/commons/8/89/PNG-Gradient.png" alt="PSN Card Image">
    </div>

    <script>
        // URL of the image
        const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/8/89/PNG-Gradient.png";

        // Refresh interval in milliseconds
        const refreshFrequency = 1800000; // 30 minutes

        // Function to update the image source
        function refreshImage() {
            const imageElement = document.getElementById("psn-card");
            // Add a unique query string to force reload without caching
            imageElement.src = `${imageUrl}?t=${Date.now()}`;
        }

        // Set an interval to refresh the image
        setInterval(refreshImage, refreshFrequency);
    </script>
</body>
</html>

to run shell scripts you'd use runShellScript

let shellScript = `/path/to/your/ip.widget/ip.sh`;


let shellScriptWrapper = {
    script: shellScript
};

// this will execute the Shell Script and store the result in the result variable.
let result = await runShellScript(shellScriptWrapper);

runShellScript is available from within webviews as well as in item scripts:
https://docs.folivora.ai/docs/10_3_webview_scripts.html

https://docs.folivora.ai/docs/1106_java_script.html

Here is a ChatGPT converted version of your linked example (you would still need to adapt the path to the ip.sh file) . This was the prompt:

Can you convert this to standard html & javascript?
To run shell scripts you can assume a runShellScript function is available and make use of it, example:
let result = await runShellScript({script: "say hello"})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IP Widget</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            font-family: 'Fira Code Retina', monospace;
            color: #fff;
            background-color: #000;
        }
        #ip-widget {
            position: absolute;
            bottom: 5px;
            left: 5px;
            font-weight: 100;
            font-size: 12px;
        }
        span {
            color: rgba(255, 255, 255, 0.8);
        }
    </style>
</head>
<body>
    <div id="ip-widget">
        Loading...
    </div>

    <script>
        const refreshFrequency = 10000; // Refresh every 10 seconds

        async function fetchIPData() {
            try {
                // Run the shell script to fetch IP data
                const result = await runShellScript({ script: "ip.widget/ip.sh" });

                // Parse the output assuming it's a JSON structure
                const data = JSON.parse(result);
                const interfaces = data.interfaces;

                // Construct the table HTML
                let tableHtml = '<table><tbody>';
                interfaces.forEach(iface => {
                    const cidr = iface.cidr ? `/${iface.cidr}` : '';
                    const router = iface.router ? ` -> ${iface.router}` : '';
                    tableHtml += `
                        <tr>
                            <td>${iface.iface}: 
                                <span>${iface.address}</span>
                                <span style="color: rgba(255,255,255,0.8);">${cidr}</span>
                                <span style="color: rgba(255,255,255,0.6);">${router}</span>
                            </td>
                        </tr>
                    `;
                });
                tableHtml += '</tbody></table>';

                // Update the widget content
                document.getElementById('ip-widget').innerHTML = tableHtml;
            } catch (error) {
                // Display error message if something goes wrong
                document.getElementById('ip-widget').innerHTML = `
                    <div>Oops: <strong>${error.message}</strong></div>
                `;
            }
        }

        // Initial fetch and set an interval for refreshing
        fetchIPData();
        setInterval(fetchIPData, refreshFrequency);
    </script>
</body>
</html>

Ok, was able to achieve same results with the proposed script in Standard Item. It again required big amount of hardcoding (menu size to be exactly same as picture size) but the script version feels like much better "template" for other widgets I have and it is cleaner with the refresh. Thx

(on picture, upper version is standard item and bottom version is web item)

For future reference, this is working solution. Just replace "example.png" with "your_psn_name.png" and it will work
PSN_example.bttpreset (12.9 KB)

1 Like

is the "runShellScript" working in current alpha versions? I am getting the SyntaxError: Unexpected identifier 'runShellScript'. Expected ';' after variable declaration. Even for the "Running Shell Scripts from the WebView" example from here: Apple Scripts & Shell Scripts in the webview · GitBook

runShellScript_not_working.bttpreset (24.4 KB)

In general it works fine, however there is an issue I noticed with your export:

While you had the standard item script deactivated, the webview item did still execute it, resulting in the error (because there is a syntax error in there). I'll fix that.

This is now fixed in 4.902 alpha (uploading)

Yes, that worked :+1:t2:

is there way to show one floating menu on all connected displays?

There is currently no such option (you'd need to duplicate the menu). However it is planned.

2 Likes