From c5ca8b7d7ac1aa751c1bf79ad35b178f96b3ba77 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 23 Jun 2026 19:34:01 -0400 Subject: feat(theme-studio): locate preview elements by hover and click Hovering a data-face preview element shows its section, face, and effective value in the preview-label info line, and the element's title carries the full record: effective fg/bg plus a per-attribute source note (direct, inherited-from-X, default, or cleared-rendering-as-default). Clicking an on-pane element scrolls to and flashes its assignment row. Off-pane and cross-surface elements stay hover-only. A single owner-qualified registry keyed by {owner, face} backs both data-face surfaces, package and UI, so the same face name under two owners never collides. The pure helpers in app-core.js take all state as arguments and return data. The one stateful adapter, previewSpan, lives in previews.js and emits the escaped markup. os() stays a package-owner wrapper over previewSpan, and a unified locateClick dispatcher replaces the per-surface click branches. Covered by test-locate.mjs and four new browser gates. Full harness green. --- scripts/theme-studio/previews.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'scripts/theme-studio/previews.js') diff --git a/scripts/theme-studio/previews.js b/scripts/theme-studio/previews.js index bef8b7c12..cb9d5babe 100644 --- a/scripts/theme-studio/previews.js +++ b/scripts/theme-studio/previews.js @@ -3,7 +3,27 @@ // they reference shared globals (PKGMAP, MAP, faceCss, effFg, ...) and are // inlined into the page's single script element via the PREVIEWS_J token in app.js. function ofs(app,face){const f=PKGMAP[app][face]||{},fg=effFg(pkgEffFg(app,face)),bg=pkgEffBg(app,face);return faceCss(f,fg,bg,{fontSize:(f.height||1),boxBg:bg||MAP['bg']});} -function os(app,face,txt){return `${txt}`;} +// The CSS for a UI-owned face rendered off any preview surface: effective fg +// (floored to the default fg) and bg, following the built-in UI inherit chain so +// the rendered color matches what the registry reports. The @ui counterpart to ofs. +function ulocateCss(face){const o=UIMAP[face]||{},fg=effFg(resolveUiAttr(face,'fg',UIMAP)),bg=resolveUiAttr(face,'bg',UIMAP)||null;return faceCss(o,fg,bg,{boxBg:bg||MAP['bg']});} +// previewSpan -- the one stateful locate adapter (preview-locate spec). Reads the +// live globals (PKGMAP / UIMAP / MAP), dispatches by the owner's surface to the +// package (ofs / PKGMAP) or @ui (UIMAP) style path, and emits the shared locate +// attributes: data-owner-app (the internal owner key), data-face, and the +// locate-onpane class when the owner is the pane currently viewed. TEXT is trusted +// preview HTML -- callers pre-escape entities, matching the old os() contract, so +// previewSpan does not re-escape it (that would double-escape < etc.). os +// delegates here for package owners; an @ui or cross-package owner makes an +// off-pane, hover-only span. +function attresc(s){return esc(String(s)).replace(/"/g,'"');} +function previewSpan(owner,face,text){ + const style=owner==='@ui'?ulocateCss(face):ofs(owner,face); + const cls=isLocateOnPane(owner,curApp())?' class="locate-onpane"':''; + const title=attresc(formatLocateTitle(locateFaceMeta(owner,face,LOCATE_REG))); + return `${text}`; +} +function os(app,face,txt){return previewSpan(app,face,txt);} // Shared wrapper for the line-based package previews: a monospace pre block. // Each renderer builds its own L array of os(...) lines and returns previewLines(L). function previewLines(L){return `
${L.join('\n')}
`;} -- cgit v1.2.3