domma-cms 0.38.1 → 0.38.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/update.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * preserving all user content, config, and custom plugins.
9
9
  */
10
10
 
11
- import {cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync,} from 'node:fs';
11
+ import {cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync,} from 'node:fs';
12
12
  import {spawnSync} from 'node:child_process';
13
13
  import path from 'node:path';
14
14
  import {createInterface} from 'node:readline';
@@ -199,7 +199,11 @@ export default async function update(_positional, flags) {
199
199
 
200
200
  info('Upstream directories that would be replaced:');
201
201
  for (const dir of UPSTREAM_DIRS) {
202
- info(` → ${dir}/`);
202
+ if (dir === 'public') {
203
+ info(` → ${dir}/ (upstream entries only — site-added files are preserved)`);
204
+ } else {
205
+ info(` → ${dir}/`);
206
+ }
203
207
  }
204
208
  console.log('');
205
209
 
@@ -303,6 +307,23 @@ export default async function update(_positional, flags) {
303
307
  const dest = path.join(cwd, dir);
304
308
  const src = path.join(PACKAGE_ROOT, dir);
305
309
  if (!existsSync(src)) continue;
310
+ if (dir === 'public') {
311
+ // public/ is shared ground: upstream ships css/ and js/, but sites
312
+ // add their own subdirs and files (games, images, downloads). Replace
313
+ // only the entries the package ships; everything else stays put.
314
+ mkdirSync(dest, {recursive: true});
315
+ for (const entry of readdirSync(src)) {
316
+ const destEntry = path.join(dest, entry);
317
+ let st = null;
318
+ try { st = lstatSync(destEntry); } catch { /* missing */ }
319
+ // A symlinked entry (e.g. a manager-shared public/js) is owned by
320
+ // whatever it points at — replacing it would silently fork it.
321
+ if (st && st.isSymbolicLink()) continue;
322
+ if (st) rmSync(destEntry, {recursive: true, force: true});
323
+ cpSync(path.join(src, entry), destEntry, {recursive: true});
324
+ }
325
+ continue;
326
+ }
306
327
  if (existsSync(dest)) rmSync(dest, {recursive: true, force: true});
307
328
  cpSync(src, dest, {recursive: true});
308
329
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.38.1",
3
+ "version": "0.38.3",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -1,2 +1,2 @@
1
- (function(){"use strict";const E=document.createElement("style");E.textContent=["[data-ctx], [data-collection-table] { cursor: context-menu; }","[data-ctx] a, [data-ctx] button, [data-collection-table] a, [data-collection-table] button,","[data-ctx] input, [data-collection-table] input { cursor: pointer; }",'[data-ctx] input[type="text"], [data-collection-table] input[type="text"] { cursor: text; }',".dm-ctx-hidden { display: none !important; }",".dm-ctx-menu { position: fixed; z-index: 10000; min-width: 200px; max-width: 300px; max-height: 60vh; overflow-y: auto;background: var(--dm-surface, #1c1c30); border: 1px solid var(--dm-border, #2a2a40); border-radius: 6px;box-shadow: var(--dm-shadow-lg, 0 8px 24px rgba(0,0,0,.4)); padding: .3rem; font-size: .83rem;color: var(--dm-text, #e5e5e5); }",".dm-ctx-menu button.dm-ctx-row { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;background: none; border: none; color: inherit; font: inherit; padding: .35rem .6rem; border-radius: 4px; cursor: pointer; }",".dm-ctx-menu button.dm-ctx-row:hover { background: var(--dm-surface-2, rgba(255,255,255,.06)); }",".dm-ctx-heading { padding: .3rem .6rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-input { width: 100%; font-size: .8rem; padding: .3rem .5rem; border-radius: 4px;border: 1px solid var(--dm-border, #2a2a40); background: var(--dm-surface-2, rgba(255,255,255,.04)); color: inherit; cursor: text; }",".dm-ctx-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0 0 .6rem; }",".dm-ctx-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; padding: .2rem .6rem;border-radius: 999px; background: var(--dm-surface-2, rgba(255,255,255,.08)); border: 1px solid var(--dm-border, #2a2a40); }",".dm-ctx-chip button { background: none; border: none; cursor: pointer; color: inherit; font-size: .9rem; line-height: 1; padding: 0; }",".dm-ctx-empty-note { padding: .75rem; font-size: .85rem; color: var(--dm-text-muted, #9a9aac); }"].join(`
2
- `),document.head.appendChild(E);function p(t){if(t._dmTable)return{fields:t._dmTable.columns.filter(e=>e.key!=="_cta").map(e=>({name:e.key,label:e.title||e.key})),entries:t._dmTable.rows.map((e,n)=>({id:String(n),data:e}))};if(t._dmCtxModel)return t._dmCtxModel;try{t._dmCtxModel=JSON.parse(atob(t.dataset.ctx))}catch{t._dmCtxModel=null}return t._dmCtxModel}function A(t,e){if(e.field==="*"){const o=String(e.value).toLowerCase();return Object.values(t.data||{}).some(r=>String(r??"").toLowerCase().includes(o))}const n=String(t.data?.[e.field]??"");switch(e.op){case"eq":return n===String(e.value);case"ne":return n!==String(e.value);case"contains":return n.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const o=parseFloat(n),r=parseFloat(e.value);if(!Number.isNaN(o)&&!Number.isNaN(r))return e.op==="gt"?o>r:o<r;const i=Date.parse(n),a=Date.parse(e.value);return!Number.isNaN(i)&&!Number.isNaN(a)?e.op==="gt"?i>a:i<a:e.op==="gt"?n>String(e.value):n<String(e.value)}default:return!0}}function x(t){const e=t._dmCtxFilters||[],n=p(t);if(!n)return;const o=n.entries.filter(i=>e.every(a=>A(i,a))),r=new Set(o.map(i=>i.id));t._dmTable?t._dmTable.render(o.map(i=>i.data)):t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems?t._dmCtx.instance.setItems(t._dmCtx.items.filter(i=>r.has(String(i._entryId)))):t._dmCtxSlides?O(t,r):t.querySelectorAll("[data-entry-id]").forEach(i=>{i.classList.toggle("dm-ctx-hidden",!r.has(i.dataset.entryId))}),H(t,e),q(t,o.length,e.length),F()}const N="dmf";function S(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function D(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function k(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return JSON.parse(decodeURIComponent(escape(atob(e))))}function F(){const t={};S().forEach((o,r)=>{o._dmCtxFilters&&o._dmCtxFilters.length&&(t[r]=o._dmCtxFilters)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(o=>o&&!o.startsWith(N+"="));Object.keys(t).length&&e.push(N+"="+D(t));const n=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",n)}catch{}}function I(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return k(t[1])}catch{return null}}function y(t=0){const e=I();if(!e)return;const n=S();let o=!1;for(const[r,i]of Object.entries(e)){const a=n[Number(r)];if(!a||!Array.isArray(i)||!i.length)continue;if(a.hasAttribute("data-collection-table")?!a._dmTable:a.classList.contains("dm-progression")?!a._dmCtx:a.classList.contains("carousel")?!a._dmCtxSlides:!1){o=!0;continue}a._dmCtxFilters=i.filter(c=>c&&c.field&&c.op!=null&&"value"in c),x(a)}o&&t<20&&setTimeout(()=>y(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(y,0)):setTimeout(y,0);function O(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const n=document.createElement("div");n.className="carousel-track",t._dmCtxSlides.forEach(o=>{e.has(o.dataset.entryId)&&n.appendChild(o.cloneNode(!0))}),t.replaceChildren(n),n.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}const z={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function H(t,e){let n=t._dmCtxBar;if(!e.length){n&&(n.remove(),t._dmCtxBar=null);return}n||(n=document.createElement("div"),n.className="dm-ctx-chips",t.parentNode.insertBefore(n,t),t._dmCtxBar=n),n.replaceChildren(),e.forEach((r,i)=>{const a=document.createElement("span");a.className="dm-ctx-chip";const s=String(r.value),c=s.length>24?s.slice(0,24)+"\u2026":s;a.appendChild(document.createTextNode(r.field==="*"?`search \u201C${c}\u201D`:`${r.label||r.field} ${z[r.op]||r.op} \u201C${c}\u201D`));const d=document.createElement("button");d.type="button",d.textContent="\xD7",d.title="Remove filter",d.addEventListener("click",()=>{t._dmCtxFilters.splice(i,1),x(t)}),a.appendChild(d),n.appendChild(a)});const o=document.createElement("button");o.type="button",o.className="dm-ctx-chip",o.style.cursor="pointer",o.textContent="Clear all",o.addEventListener("click",()=>{t._dmCtxFilters=[],x(t)}),n.appendChild(o)}function q(t,e,n){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!n)return;const o=document.createElement("div");o.className="dm-ctx-empty-note",o.textContent="No entries match the current filters.",t.appendChild(o),t._dmCtxEmptyNote=o}let u=null;function f(){u&&(u.remove(),u=null,document.removeEventListener("mousedown",L,!0),document.removeEventListener("keydown",T,!0),window.removeEventListener("scroll",M,!0))}const L=t=>{u&&!u.contains(t.target)&&f()},T=t=>{t.key==="Escape"&&f()},M=t=>{u&&!u.contains(t.target)&&f()};function h(t,e){const n=document.createElement("button");return n.type="button",n.className="dm-ctx-row",n.textContent=t,n.addEventListener("click",e),n}function $(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}const g=(t,e=24)=>{const n=String(t);return n.length>e?n.slice(0,e)+"\u2026":n};function B(t,e,n,o,r){f();const i=p(n);if(!i)return;const a=document.createElement("div");a.className="dm-ctx-menu",u=a;const s=(l,m,v,_)=>{(n._dmCtxFilters=n._dmCtxFilters||[]).push({field:l,label:m,op:v,value:_}),x(n),f()};if(r&&o){const l=o.data?.[r.name]??"";a.appendChild($(r.label||r.name)),a.appendChild(h(`Filter: = \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"eq",l))),a.appendChild(h(`Filter: \u2260 \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"ne",l)));const m=String(l);(m!==""&&!Number.isNaN(parseFloat(m))||!Number.isNaN(Date.parse(m)))&&(a.appendChild(h(`Filter: > \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"gt",l))),a.appendChild(h(`Filter: < \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"lt",l))))}else if(o){a.appendChild($("Filter by"));for(const l of i.fields){const m=o.data?.[l.name];m==null||String(m)===""||a.appendChild(h(`${l.label||l.name}: \u201C${g(m,20)}\u201D`,()=>s(l.name,l.label,"eq",m)))}}const c=document.createElement("div");c.style.padding=".35rem .6rem";const d=document.createElement("input");d.type="text",d.className="dm-ctx-input",d.placeholder="Search\u2026",d.addEventListener("mousedown",l=>l.stopPropagation()),d.addEventListener("keydown",l=>{l.key==="Enter"&&d.value.trim()&&s("*","search","contains",d.value.trim())}),c.appendChild(d),a.appendChild(c);const b=n._dmCtxFilters||[];if(b.length){const l=document.createElement("div");l.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",a.appendChild(l),a.appendChild(h(`Clear all filters (${b.length})`,()=>{n._dmCtxFilters=[],x(n),f()}))}document.body.appendChild(a);const C=a.getBoundingClientRect();a.style.left=Math.min(t,window.innerWidth-C.width-8)+"px",a.style.top=Math.min(e,window.innerHeight-C.height-8)+"px",document.addEventListener("mousedown",L,!0),document.addEventListener("keydown",T,!0),window.addEventListener("scroll",M,!0),setTimeout(()=>d.focus(),0)}function j(t,e,n){if(t._dmTable){const s=e.closest("tbody tr");if(!s)return{entry:null,field:null};const c=e.closest("td"),d=s.querySelectorAll("td"),b=Array.from(d).map(m=>m.textContent.trim()),C=n.entries.find(m=>n.fields.every((v,_)=>String(m.data?.[v.name]??"").trim()===(b[_]??"").trim()))||null,l=c&&n.fields[c.cellIndex]||null;return{entry:C,field:l}}const o=e.closest("[data-entry-id]");if(!o)return{entry:null,field:null};const r=n.entries.find(s=>s.id===o.dataset.entryId)||null;if(!r)return{entry:null,field:null};const i=(e.textContent||"").trim().slice(0,80);let a=null;if(i){const s=n.fields.filter(c=>{const d=String(r.data?.[c.name]??"");return d&&(d.includes(i)||i.includes(d))});s.length===1&&(a=s[0])}return{entry:r,field:a}}document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const n=p(e);if(!n||!n.fields?.length)return;t.preventDefault();const{entry:o,field:r}=j(e,t.target,n);B(t.clientX,t.clientY,e,o,r)})})();
1
+ (function(){"use strict";const O='url("data:image/svg+xml,'+encodeURIComponent("<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><path d='M5 2v15.5l4.2-4 2.4 5.4 2.8-1.2-2.4-5.3 5.5-.1z' fill='#fff' stroke='#000' stroke-width='1.2' stroke-linejoin='round'/><path d='M13.5 15h9.5l-3.6 4.2v4l-2.3-1.8v-2.2z' fill='#5b8cff' stroke='#fff' stroke-width='1' stroke-linejoin='round'/></svg>")+'") 5 3, context-menu',E=document.createElement("style");E.textContent=["[data-ctx], [data-collection-table] { cursor: "+O+"; }","[data-ctx] a, [data-ctx] button, [data-collection-table] a, [data-collection-table] button,","[data-ctx] input, [data-collection-table] input { cursor: pointer; }",'[data-ctx] input[type="text"], [data-collection-table] input[type="text"] { cursor: text; }',".dm-ctx-hidden { display: none !important; }",".dm-ctx-menu { position: fixed; z-index: 10000; min-width: 200px; max-width: 300px; max-height: 60vh; overflow-y: auto;background: var(--dm-surface, #1c1c30); border: 1px solid var(--dm-border, #2a2a40); border-radius: 6px;box-shadow: var(--dm-shadow-lg, 0 8px 24px rgba(0,0,0,.4)); padding: .3rem; font-size: .83rem;color: var(--dm-text, #e5e5e5); }",".dm-ctx-menu button.dm-ctx-row { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;background: none; border: none; color: inherit; font: inherit; padding: .35rem .6rem; border-radius: 4px; cursor: pointer; }",".dm-ctx-menu button.dm-ctx-row:hover { background: var(--dm-surface-2, rgba(255,255,255,.06)); }",".dm-ctx-heading { padding: .3rem .6rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-input { width: 100%; font-size: .8rem; padding: .3rem .5rem; border-radius: 4px;border: 1px solid var(--dm-border, #2a2a40); background: var(--dm-surface-2, rgba(255,255,255,.04)); color: inherit; cursor: text; }",".dm-ctx-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0 0 .6rem; }",".dm-ctx-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; padding: .2rem .6rem;border-radius: 999px; background: var(--dm-surface-2, rgba(255,255,255,.08)); border: 1px solid var(--dm-border, #2a2a40); }",".dm-ctx-chip button { background: none; border: none; cursor: pointer; color: inherit; font-size: .9rem; line-height: 1; padding: 0; }",".dm-ctx-empty-note { padding: .75rem; font-size: .85rem; color: var(--dm-text-muted, #9a9aac); }"].join(`
2
+ `),document.head.appendChild(E);function p(t){if(t._dmTable)return{fields:t._dmTable.columns.filter(e=>e.key!=="_cta").map(e=>({name:e.key,label:e.title||e.key})),entries:t._dmTable.rows.map((e,n)=>({id:String(n),data:e}))};if(t._dmCtxModel)return t._dmCtxModel;try{t._dmCtxModel=JSON.parse(atob(t.dataset.ctx))}catch{t._dmCtxModel=null}return t._dmCtxModel}function R(t,e){if(e.field==="*"){const o=String(e.value).toLowerCase();return Object.values(t.data||{}).some(r=>String(r??"").toLowerCase().includes(o))}const n=String(t.data?.[e.field]??"");switch(e.op){case"eq":return n===String(e.value);case"ne":return n!==String(e.value);case"contains":return n.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const o=parseFloat(n),r=parseFloat(e.value);if(!Number.isNaN(o)&&!Number.isNaN(r))return e.op==="gt"?o>r:o<r;const i=Date.parse(n),a=Date.parse(e.value);return!Number.isNaN(i)&&!Number.isNaN(a)?e.op==="gt"?i>a:i<a:e.op==="gt"?n>String(e.value):n<String(e.value)}default:return!0}}function x(t){const e=t._dmCtxFilters||[],n=p(t);if(!n)return;const o=n.entries.filter(i=>e.every(a=>R(i,a))),r=new Set(o.map(i=>i.id));t._dmTable?t._dmTable.render(o.map(i=>i.data)):t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems?t._dmCtx.instance.setItems(t._dmCtx.items.filter(i=>r.has(String(i._entryId)))):t._dmCtxSlides?I(t,r):t.querySelectorAll("[data-entry-id]").forEach(i=>{i.classList.toggle("dm-ctx-hidden",!r.has(i.dataset.entryId))}),H(t,e),j(t,o.length,e.length),z()}const N="dmf";function S(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function $(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function A(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return JSON.parse(decodeURIComponent(escape(atob(e))))}function z(){const t={};S().forEach((o,r)=>{o._dmCtxFilters&&o._dmCtxFilters.length&&(t[r]=o._dmCtxFilters)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(o=>o&&!o.startsWith(N+"="));Object.keys(t).length&&e.push(N+"="+$(t));const n=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",n)}catch{}}function D(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return A(t[1])}catch{return null}}function v(t=0){const e=D();if(!e)return;const n=S();let o=!1;for(const[r,i]of Object.entries(e)){const a=n[Number(r)];if(!a||!Array.isArray(i)||!i.length)continue;if(a.hasAttribute("data-collection-table")?!a._dmTable:a.classList.contains("dm-progression")?!a._dmCtx:a.classList.contains("carousel")?!a._dmCtxSlides:!1){o=!0;continue}a._dmCtxFilters=i.filter(c=>c&&c.field&&c.op!=null&&"value"in c),x(a)}o&&t<20&&setTimeout(()=>v(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(v,0)):setTimeout(v,0);function I(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const n=document.createElement("div");n.className="carousel-track",t._dmCtxSlides.forEach(o=>{e.has(o.dataset.entryId)&&n.appendChild(o.cloneNode(!0))}),t.replaceChildren(n),n.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}const F={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function H(t,e){let n=t._dmCtxBar;if(!e.length){n&&(n.remove(),t._dmCtxBar=null);return}n||(n=document.createElement("div"),n.className="dm-ctx-chips",t.parentNode.insertBefore(n,t),t._dmCtxBar=n),n.replaceChildren(),e.forEach((r,i)=>{const a=document.createElement("span");a.className="dm-ctx-chip";const s=String(r.value),c=s.length>24?s.slice(0,24)+"\u2026":s;a.appendChild(document.createTextNode(r.field==="*"?`search \u201C${c}\u201D`:`${r.label||r.field} ${F[r.op]||r.op} \u201C${c}\u201D`));const d=document.createElement("button");d.type="button",d.textContent="\xD7",d.title="Remove filter",d.addEventListener("click",()=>{t._dmCtxFilters.splice(i,1),x(t)}),a.appendChild(d),n.appendChild(a)});const o=document.createElement("button");o.type="button",o.className="dm-ctx-chip",o.style.cursor="pointer",o.textContent="Clear all",o.addEventListener("click",()=>{t._dmCtxFilters=[],x(t)}),n.appendChild(o)}function j(t,e,n){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!n)return;const o=document.createElement("div");o.className="dm-ctx-empty-note",o.textContent="No entries match the current filters.",t.appendChild(o),t._dmCtxEmptyNote=o}let u=null;function f(){u&&(u.remove(),u=null,document.removeEventListener("mousedown",L,!0),document.removeEventListener("keydown",k,!0),window.removeEventListener("scroll",M,!0))}const L=t=>{u&&!u.contains(t.target)&&f()},k=t=>{t.key==="Escape"&&f()},M=t=>{u&&!u.contains(t.target)&&f()};function h(t,e){const n=document.createElement("button");return n.type="button",n.className="dm-ctx-row",n.textContent=t,n.addEventListener("click",e),n}function T(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}const g=(t,e=24)=>{const n=String(t);return n.length>e?n.slice(0,e)+"\u2026":n};function q(t,e,n,o,r){f();const i=p(n);if(!i)return;const a=document.createElement("div");a.className="dm-ctx-menu",u=a;const s=(l,m,y,_)=>{(n._dmCtxFilters=n._dmCtxFilters||[]).push({field:l,label:m,op:y,value:_}),x(n),f()};if(r&&o){const l=o.data?.[r.name]??"";a.appendChild(T(r.label||r.name)),a.appendChild(h(`Filter: = \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"eq",l))),a.appendChild(h(`Filter: \u2260 \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"ne",l)));const m=String(l);(m!==""&&!Number.isNaN(parseFloat(m))||!Number.isNaN(Date.parse(m)))&&(a.appendChild(h(`Filter: > \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"gt",l))),a.appendChild(h(`Filter: < \u201C${g(l)}\u201D`,()=>s(r.name,r.label,"lt",l))))}else if(o){a.appendChild(T("Filter by"));for(const l of i.fields){const m=o.data?.[l.name];m==null||String(m)===""||a.appendChild(h(`${l.label||l.name}: \u201C${g(m,20)}\u201D`,()=>s(l.name,l.label,"eq",m)))}}const c=document.createElement("div");c.style.padding=".35rem .6rem";const d=document.createElement("input");d.type="text",d.className="dm-ctx-input",d.placeholder="Search\u2026",d.addEventListener("mousedown",l=>l.stopPropagation()),d.addEventListener("keydown",l=>{l.key==="Enter"&&d.value.trim()&&s("*","search","contains",d.value.trim())}),c.appendChild(d),a.appendChild(c);const b=n._dmCtxFilters||[];if(b.length){const l=document.createElement("div");l.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",a.appendChild(l),a.appendChild(h(`Clear all filters (${b.length})`,()=>{n._dmCtxFilters=[],x(n),f()}))}document.body.appendChild(a);const C=a.getBoundingClientRect();a.style.left=Math.min(t,window.innerWidth-C.width-8)+"px",a.style.top=Math.min(e,window.innerHeight-C.height-8)+"px",document.addEventListener("mousedown",L,!0),document.addEventListener("keydown",k,!0),window.addEventListener("scroll",M,!0),setTimeout(()=>d.focus(),0)}function B(t,e,n){if(t._dmTable){const s=e.closest("tbody tr");if(!s)return{entry:null,field:null};const c=e.closest("td"),d=s.querySelectorAll("td"),b=Array.from(d).map(m=>m.textContent.trim()),C=n.entries.find(m=>n.fields.every((y,_)=>String(m.data?.[y.name]??"").trim()===(b[_]??"").trim()))||null,l=c&&n.fields[c.cellIndex]||null;return{entry:C,field:l}}const o=e.closest("[data-entry-id]");if(!o)return{entry:null,field:null};const r=n.entries.find(s=>s.id===o.dataset.entryId)||null;if(!r)return{entry:null,field:null};const i=(e.textContent||"").trim().slice(0,80);let a=null;if(i){const s=n.fields.filter(c=>{const d=String(r.data?.[c.name]??"");return d&&(d.includes(i)||i.includes(d))});s.length===1&&(a=s[0])}return{entry:r,field:a}}document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const n=p(e);if(!n||!n.fields?.length)return;t.preventDefault();const{entry:o,field:r}=B(e,t.target,n);q(t.clientX,t.clientY,e,o,r)})})();
@@ -117,7 +117,7 @@
117
117
 
118
118
  <!-- Site initialisation -->
119
119
  <script src="/public/js/site.js?v=20260722-collection-ctx" type="module"></script>
120
- <script src="/public/js/collection-context.js?v=20260722-ctx-hash" defer></script>
120
+ <script src="/public/js/collection-context.js?v=20260722-ctx-cursor" defer></script>
121
121
 
122
122
  <!-- Interactive Collection Browser (used by [collection] shortcodes with
123
123
  searchable / filterable / sortable / paginate attributes) -->