Skip to content

Commit 0220d77

Browse files
jeff-zuckerclaude
andcommitted
sol-pod: auto-init + last-visited memory + LDP metadata; cross-window auth signaling
sol-pod - auto-initializes in connectedCallback (microtask-deferred); initialize() is now single-flight so explicit awaiters share the in-flight pass. - Remembers the last-visited container per (pods-group, side) in localStorage and restores it on the next mount. If the remembered path sits under a different storage than pods[0], the dropdown switches to that pod via _rootForPath. Storage-unavailable contexts are wrapped silently. - Container items now carry size / mtime / modified / types from the LDP listing (posix:size, posix:mtime, dct:modified, rdf:type). Item shape stays additive — existing url/name/displayName/isContainer/ contentType unchanged. - Breadcrumb gear routes through _activateItem so a host's podClickAction (e.g. dk-solidos navigating its iframe) gets first refusal on the current container; falls back to the pod-ops modal. - gear-icon attribute applies to both per-item and breadcrumb gears via a shared _paintGearIcon helper. sol-login - BroadcastChannel('sol-auth') signals login/logout across same-origin windows, tabs, and iframes. Foreign auth sets `external-auth` on the host so CSS paints the button green and surfaces the chip — only when this element has no own logged-in session. sol-menu / sol-include / sol-weather - sol-menu: part="content"/part="nav" hooks for outside theming. Default .sol-menu-content overflow flipped to hidden (chrome doesn't scroll); horizontal nav now wraps instead of scrolling when items don't fit a row. - sol-include: :host is flex column with flex:1 1 auto + min-height:0, and the .si-content wrapper gets the same treatment in both shadow and trusted (slotted) modes — so components placed inside finally get a definite height to fill and scroll on their own. - sol-weather: hide the card on fetch error instead of surfacing the "Failed to fetch" string. Help / demos - New help/newhelp/ tabbed help pages (pod / menu / query / search / weather). index.html wires podClickAction via a MutationObserver that also watches sol-menu's shadow subtree so clicks on the live sol-pod render item details — including the new size / mtime / modified / types fields, with sizes humanized. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 4fe9587 commit 0220d77

27 files changed

Lines changed: 938 additions & 51 deletions

core/pod-ops.js

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import { rdf } from './rdf.js';
88
const LDP_CONTAINS = 'http://www.w3.org/ns/ldp#contains';
99
const OWL_SAME_AS = 'http://www.w3.org/2002/07/owl#sameAs';
1010
const PIM_STORAGE = 'http://www.w3.org/ns/pim/space#storage';
11+
// Per-resource metadata predicates emitted by Solid LDP servers in
12+
// container listings. POSIX_* is the canonical pair (CSS, and NSS via
13+
// the same URI even though it imports as `stat:`). DCT_MODIFIED is
14+
// the human-readable ISO-datetime mirror.
15+
const POSIX_SIZE = 'http://www.w3.org/ns/posix/stat#size';
16+
const POSIX_MTIME = 'http://www.w3.org/ns/posix/stat#mtime';
17+
const DCT_MODIFIED = 'http://purl.org/dc/terms/modified';
18+
const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
1119

1220
// ── MIME types ────────────────────────────────────────────────────────
1321

@@ -58,32 +66,56 @@ async function fetchContainerRaw(url, fetchFn) {
5866

5967
const store = rdf.graph();
6068
rdf.parse(text, store, url, 'text/turtle');
61-
return store.each(rdf.sym(url), rdf.sym(LDP_CONTAINS), null, null).map(n => n.value);
69+
const container = rdf.sym(url);
70+
return store.each(container, rdf.sym(LDP_CONTAINS), null, null).map(n => {
71+
const sub = rdf.sym(n.value);
72+
const num = (pred) => {
73+
const v = store.any(sub, rdf.sym(pred), null, null);
74+
if (!v) return null;
75+
const x = Number(v.value);
76+
return Number.isFinite(x) ? x : null;
77+
};
78+
const str = (pred) => {
79+
const v = store.any(sub, rdf.sym(pred), null, null);
80+
return v ? v.value : null;
81+
};
82+
return {
83+
url: n.value,
84+
size: num(POSIX_SIZE),
85+
mtime: num(POSIX_MTIME),
86+
modified: str(DCT_MODIFIED),
87+
types: store.each(sub, rdf.sym(RDF_TYPE), null, null).map(t => t.value),
88+
};
89+
});
6290
}
6391

6492
function tryDecode(s) {
6593
try { return decodeURIComponent(s); } catch { return s; }
6694
}
6795

68-
function mapResources(resourceUrls) {
69-
return resourceUrls.map(url => {
96+
function mapResources(resources) {
97+
return resources.map(r => {
98+
const url = r.url;
7099
const isContainer = url.endsWith('/');
71100
const name = isContainer ? url.split('/').slice(-2)[0] : url.split('/').pop();
72101
const displayName = tryDecode(name);
73102
// Inferred from the extension (containers are turtle). It's a guess —
74103
// an extensionless file is 'application/octet-stream'; HEAD the url for
75104
// the server's authoritative Content-Type.
76105
const contentType = isContainer ? 'text/turtle' : contentTypeFor(name);
77-
return { url, name, displayName, isContainer, contentType };
106+
return {
107+
url, name, displayName, isContainer, contentType,
108+
size: r.size, mtime: r.mtime, modified: r.modified, types: r.types,
109+
};
78110
}).sort((a, b) => {
79111
if (a.isContainer === b.isContainer) return a.displayName.localeCompare(b.displayName);
80112
return a.isContainer ? -1 : 1;
81113
});
82114
}
83115

84116
export async function fetchContainer(url, fetchFn) {
85-
const urls = await fetchContainerRaw(url, fetchFn);
86-
return mapResources(urls);
117+
const resources = await fetchContainerRaw(url, fetchFn);
118+
return mapResources(resources);
87119
}
88120

89121
// ── File operations ───────────────────────────────────────────────────
@@ -114,9 +146,9 @@ export async function copyFolder(sourceUrl, targetContainerUrl, folderName, fetc
114146
if (onProgress) onProgress(`Copying folder ${folderName}...`);
115147

116148
const sourceFetch = fetchFnForUrl(sourceUrl);
117-
let childUrls;
149+
let children;
118150
try {
119-
childUrls = await fetchContainerRaw(sourceUrl, sourceFetch);
151+
children = await fetchContainerRaw(sourceUrl, sourceFetch);
120152
} catch (e) {
121153
return { success: false, error: e.message };
122154
}
@@ -131,7 +163,8 @@ export async function copyFolder(sourceUrl, targetContainerUrl, folderName, fetc
131163
}
132164

133165
let failed = 0;
134-
for (const childUrl of childUrls) {
166+
for (const child of children) {
167+
const childUrl = child.url;
135168
const isContainer = childUrl.endsWith('/');
136169
const name = isContainer ? childUrl.split('/').slice(-2)[0] : childUrl.split('/').pop();
137170
if (isContainer) {

dist/podz-extras.bundle.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sol-full.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sol-include.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sol-login.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sol-menu.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sol-pod-ops.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sol-pod.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/solid-web-components.bundle.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

help/data/demo-menu.ttl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
ui:parts ( <#Home> <#DataMenu> <#About> ) .
77

88
<#Home> a ui:Link; ui:label "Home" ;
9-
ui:icon <../help/house.png> ;
109
ui:contents "<h2>Welcome</h2><p>This is the home page, rendered from a <code>ui:contents</code> literal.</p>" .
1110

1211
<#DataMenu> a ui:Menu; ui:label "Data" ;
@@ -16,11 +15,21 @@
1615
ui:name "sol-query" ;
1716
ui:attribute
1817
[ schema:name "endpoint" ; schema:value <./sample-data.ttl> ] ,
19-
[ schema:name "sparql" ; schema:value "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }" ] ,
18+
[ schema:name "sparql" ; schema:value """PREFIX schema: <http://schema.org/>
19+
SELECT ?name ?gender ?location WHERE {
20+
?p a schema:Person ;
21+
schema:name ?name ;
22+
schema:gender ?gender ;
23+
schema:homeLocation ?location .
24+
} LIMIT 3""" ] ,
2025
[ schema:name "view" ; schema:value "table" ] .
2126

2227
<#SampleTriples> a ui:Link; ui:label "People (raw triples)" ;
2328
ui:href <./sample-data.ttl> .
2429

2530
<#About> a ui:Link; ui:label "About" ;
26-
ui:contents "<h2>About</h2><p>This menu was generated entirely from RDF using the <code>from-rdf</code> attribute.</p>" .
31+
ui:href <./sample-buttons.html> ;
32+
ui:handler [
33+
ui:name "sol-include" ;
34+
ui:attribute [ schema:name "trusted" ; schema:value "" ]
35+
] .

0 commit comments

Comments
 (0)