added desktopfile plugin + icosa-gallery

This commit is contained in:
Leon van Kammen 2026-10-06 22:22:52 +02:00
parent 6fabe4aab7
commit 4dc6ff409c
12 changed files with 176 additions and 38 deletions

View file

@ -83,6 +83,7 @@ app.plugin.pubnix = app.addPlugin('plugin/pubnix')
app.plugin.diskscan = app.addPlugin('plugin/diskscan')
app.plugin.convert = app.addPlugin('plugin/convert')
app.plugin.moderation = app.addPlugin('plugin/moderation')
app.plugin.desktopfile = app.addPlugin('plugin/desktopfile')
app.plugin.xrfragments = app.addPlugin('plugin/xrfragments')
app.plugin.janusxr = app.addPlugin('plugin/janusxr')
app.plugin.clouddrive = app.addPlugin('plugin/clouddrive')

View file

@ -87,6 +87,7 @@ textarea{
input[type=submit]{
display: inline-block;
margin: 0 15px 0px 0px;
border: 1px dotted
}
.btn a,
@ -205,7 +206,7 @@ h1{
h1 a,
h2 a,
center a{
.jumbotron center a{
text-decoration:none;
border:none !important;
}
@ -219,14 +220,6 @@ table.header tr td {
border:none;
}
.btn.XR{
line-height:10px;
display: inline-block;
position: absolute;
top: 20px;
right: 2px;
}
/* logo */
.logo *{
font-size:15px;

View file

@ -1,3 +1,7 @@
:root{
--gradient-1: #114,#224,#114,#3367,#424,#224;
}
hr{
display:none !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View file

@ -0,0 +1,61 @@
<h1>Hi there! 👋</h1>
Welcome to this XR experience.<br/>
<br/>
<div class="btn script">
<button class="btn secondary" onclick="document.querySelector('#tips').style.display = ''">Tips for navigating in XR</button>
</div>
<div id="tips" style="display:none">
<br/>
<table>
<tr>
<td>
<b>Keyboard+Mouse</b>
</td>
<td>
use the WASD-keys and mousedrag to navigate
</td>
</tr>
<tr>
<td width="200">
<b>AR/VR headset</b>
</td>
<td>
walk around / use right-longpinch (or thumbstick) for local teleports<br/>
</td>
</tr>
<tr>
<td>
<b>PhoneVR</b>
</td>
<td>
use gaze or bluetooth/mouse to navigate
</td>
</tr>
<tr>
<td>
<b>PhoneAR</b>
</td>
<td>
walk around and tap screen to click
</td>
</tr>
<tr>
<td>
<b>Mobile</b>
</td>
<td>
use the WASD-keys and mousedrag to navigate
</td>
</tr>
</table>
<br/>
You've got this!
</div>
<br/>
Here's a demo to surf <a href="https://icosa.gallery/collections" target="_blank">icosa-gallery</a> immersively (without launching the website, <a href="https://openbrush.app" target="_blank">OpenBrush</a> or <a href="https://openblocks.app" target="_blank">OpenBlocks</a>).<br>
This is possible thanks to icosa-gallery supporting the <a href="https://xrfragment.org/#XRF%20microformat" target="_blank">XR Fragments microformat</a>.
<br>
> <b>NOTE</b>: most example assets are not optimized regarding scale/position/floor (but can be done in OpenBrush, OpenBlocks or Blender)

View file

@ -0,0 +1,4 @@
[Desktop Entry]
Type=Link
Name=icosa gallery
URL=https://icosa.gallery/collections

View file

@ -2,7 +2,7 @@ ${header}
{if parent ~= nil and parent.parent == nil then
print([[
<div style="height:90px;" class="jumbotron">
<div style="min-height:45px;" class="jumbotron">
<h1 style="display:none">${opts.plugin_global_title}</h1>
<br>
<center class="col3">
@ -24,6 +24,17 @@ ${header}
]])
end}
{if isroot or url:match("/dir$") then
local url = urlpath:gsub("/dir$","")
print("<center>")
if app.arg[1] == "generate" then
print([[<div class="btn XR"><a class="border secondary" href="janusweb/#janus.url=]] .. url .. [[&janus.referrer=${referrer}">Enter XR</a></div>&nbsp;]])
else
print([[<div class="btn XR"><a class="border secondary" href="]]..url..[[?view=3D">Enter XR</a></div>&nbsp;]])
end
print("</center><br/>")
end}
{if parent ~= nil and parent.parent ~= nil then
print([[
<h2><span class="col1">//</span> ${parent.name} <a href="${req.url}.rss"><img src="img/rss.svg" class="icon rss"/></a>
@ -66,7 +77,7 @@ end}
local icon = false
local url = item.markup and item.parent.url or item.url
local thumbnail = item.markup and (item.parent.thumbnail or '') or ''
if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. ');"' end
if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. '), linear-gradient(45deg,var(--gradient-1));"' end
if item.view3D
or (item.janusxr and item.janusxr.room)
or (item.redirect)
@ -75,20 +86,20 @@ end}
url = url .. '?view=3D'
icon = 'play'
if #item.thumbnail > 0 then
thumbnail = 'style="background-image: url(./img/playcover.svg), url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. ');"'
thumbnail = 'style="background-image: url(./img/playcover.svg), url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. '), linear-gradient(45deg,var(--gradient-1));"'
else
thumbnail = 'style="background-image: url(./img/playcover.svg);"'
thumbnail = 'style="background-image: url(./img/playcover.svg), linear-gradient(45deg,var(--gradient-1));"'
end
end
print([[
<div class="tile">
<a href="]] .. getURL(url) .. [[">
<a href="]] .. getURL(item.url) .. [[">
<div class="img" ]] .. util.stripPath(thumbnail) .. [[></div>
<div class="text">
]] .. ( icon and '<img src="img/' .. icon .. '.svg" class="icon ' .. icon .. '"/>' or '') .. [[<a href="]] .. getURL(item.url) .. [[">]] .. item.title .. [[</a>
]]
..
( (not playable or (playable and #item.children > 1)) and
( ((#item.children > 0 and not playable) or (playable and #item.children > 1)) and
[[
<span class="col3" style="float:right">[]] .. #item.children .. [[]</span>
]] or ''

View file

@ -91,18 +91,26 @@ return {
-- *TODO* modularize this logic into plugins better
-- prioritize markup experience above binary 3D files as experience containers
if data.item == nil or (isMarkupExperience(v) and not isMarkupExperience(data.item)) then
local notinroot = v.parent.parent ~= nil
if notinroot and data.item == nil
or (isMarkupExperience(v)
and not isMarkupExperience(data.item))
then
-- update referrer url
data.referrer = util.baseURLify(v.view3D and (v.parent.parent and v.parent.parent.url) or v.parent.url)
data.item = v
data.title = data.item.title
data.parent = v.parent
-- *TODO* modularize this logic into plugins better
data.janusurl = v.url
if not data.janusurl:match("://") then
data.janusurl = (v.janusxr and v.janusxr.wrap) and app.opts.baseurl .. util.stripPath(v.url)
-- if parent has redirect to item set, use parent url
if not data.janusurl or v.parent.redirect == v.url then
data.janusurl = app.opts.baseurl .. util.stripPath(v.parent.redirect or v.parent.url)
end
end
if v.janusxr and v.janusxr.wrap and v.janusxr.wrap.file then
-- what to do here? no generated portals?
data.janusxr = app.tpl( util.readfile(v.path), data )

View file

@ -63,13 +63,6 @@
-- janusweb workaround (it does not support '/' url somehow)
urlpath = urlpath .. app.opts.plugin_diskscan_dirname
end
if not urlpath:match('/dir$') then
if app.arg[1] == "generate" then
print([[<div class="btn XR"><a class="border secondary" href="janusweb/#janus.url=]] .. urlpath .. [[&janus.referrer=${referrer}">XR</a></div>&nbsp;]])
else
print([[<div class="btn XR"><a class="border secondary" href="${urlpath}?view=3D">XR</a></div>&nbsp;]])
end
end
end}
{if template == "page/index.html" or defined(item) then
print([[

View file

@ -13,6 +13,10 @@ else
description = opts.plugin_global_popup_instructions
end}
{if true then
playable = itemdir.view3D or (itemdir.janusxr and itemdir.janusxr.room) or (itemdir.redirect)
end}
<div id="popup" class="content">
<div class="spectrum span" style="top:-1px">
<div></div>
@ -22,6 +26,13 @@ end}
</noscript>
<img src="img/close.svg" width="20" class="icon close" onclick="document.querySelector('#popup').style.display = 'none'"/>
${description}
{if playable and #itemdir.children > 1 and (itemdir.janusxr and itemdir.janusxr.layout) then
print([[
<div class="btn">
<a href="]] .. util.baseURLify( itemdir.url .. '/dir' ) .. [[">view assets ⮩</a>
</div>
]])
end}
</div>
<script>

View file

@ -0,0 +1,45 @@
local util = require('util')
app.on('scan_experience', function(exp) end)
app.on('scan_experience_file', function(exp)
if exp.path:lower():match('.desktop$') then
local data = (util.readfile( exp.path ) or '')
local valid = false
foreach( util.split(data, '\n'), function(k,line)
if line:match("^Name=") then
exp.title = line:gsub('^Name=','')
valid = true
end
if line:match("^URL=") then
exp.url = line:gsub('^URL=','')
valid = true
end
end)
if valid then
exp.path = exp.path:gsub('%.desktop','')
exp.name = exp.name:gsub('%.desktop',''):gsub("-",' ')
--exp.dir = true
--exp.view3D = 100
app.markViewable(exp, 300)
app.addExperience(exp, exp.file.ino)
print("✅ └─ plugin/desktopfile: detected URL")
end
end
end)
app.on('set', function(k,v) end)
app.on('init', function() end)
-- this registers the plugin UI
local plugin = {
name = 'Desktop files',
hide = true,
info = '',
opts = {
}
}
return plugin

View file

@ -133,6 +133,8 @@ return {
--local already_seen = seen[ino] and seen[ino].parent.file.ino == experience.parent.file.ino and true or false -- process once
--if already_seen and not experience.symlink then return end
me.checkSideCarThumbnail(experience)
-- found directory
if not me.shouldHideFile(name) and
( (kind == unix.DT_DIR and kind ~= unix.DT_LNK) or experience.dir )
@ -141,17 +143,6 @@ return {
experience.dir = true
app.pub("scan_experience", nil, experience)
app.addExperience(experience,ino)
-- check for directory thumbnail sidecarfile
local thumbExt = {"png","jpg","webp"}
foreach( thumbExt, function(k,ext)
local files = { fullpath .. '.' .. ext, fullpath .. '/' .. name .. '.' .. ext }
foreach( files, function(kk,thumbnailFile)
if util.fileExist( thumbnailFile ) then
experience.thumbnail = "/" .. util.stripPath(thumbnailFile)
print("✅ └─ plugin/diskscan: detected .".. ext:lower() .. " thumbnail file: " .. thumbnailFile)
end
end)
end)
-- scan children
me.scanDir(fullpath,experience,seen,symlinks)
end
@ -186,5 +177,21 @@ return {
table.insert(exp.parent.children, exp.id)
end
end)
end,
checkSideCarThumbnail = function(experience)
local fullpath = experience.path
local name = experience.name
-- check for directory thumbnail sidecarfile
local thumbExt = {"png","jpg","webp"}
foreach( thumbExt, function(k,ext)
local files = { fullpath .. '.' .. ext, fullpath .. '/' .. name .. '.' .. ext }
foreach( files, function(kk,thumbnailFile)
if util.fileExist( thumbnailFile ) then
experience.thumbnail = "/" .. util.stripPath(thumbnailFile)
print("✅ └─ plugin/diskscan: detected .".. ext:lower() .. " thumbnail file: " .. thumbnailFile)
end
end)
end)
end
}