domma-cms 0.69.4 → 0.70.0

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "blog",
3
3
  "displayName": "Blog",
4
- "version": "1.4.4",
4
+ "version": "1.6.0",
5
5
  "tier": "free",
6
6
  "description": "Public-facing blog with posts, categories and comments - layouts and listings built from editable blocks.",
7
7
  "author": "Domma CMS",
@@ -34,11 +34,11 @@
34
34
  { "path": "/plugins/blog/settings", "view": "plugin-blog-settings", "title": "Blog Settings - Domma CMS" }
35
35
  ],
36
36
  "views": {
37
- "plugin-blog-posts": { "entry": "blog/admin/views/blog.js?v=7", "exportName": "blogView" },
38
- "plugin-blog-post-editor": { "entry": "blog/admin/views/post-editor.js?v=7", "exportName": "postEditorView" },
39
- "plugin-blog-categories": { "entry": "blog/admin/views/categories.js?v=7", "exportName": "categoriesView" },
40
- "plugin-blog-comments": { "entry": "blog/admin/views/comments.js?v=7", "exportName": "commentsView" },
41
- "plugin-blog-settings": { "entry": "blog/admin/views/settings.js?v=7", "exportName": "settingsView" }
37
+ "plugin-blog-posts": { "entry": "blog/admin/views/blog.js?v=8", "exportName": "blogView" },
38
+ "plugin-blog-post-editor": { "entry": "blog/admin/views/post-editor.js?v=8", "exportName": "postEditorView" },
39
+ "plugin-blog-categories": { "entry": "blog/admin/views/categories.js?v=8", "exportName": "categoriesView" },
40
+ "plugin-blog-comments": { "entry": "blog/admin/views/comments.js?v=8", "exportName": "commentsView" },
41
+ "plugin-blog-settings": { "entry": "blog/admin/views/settings.js?v=8", "exportName": "settingsView" }
42
42
  }
43
43
  }
44
44
  }
@@ -19,7 +19,7 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
19
19
  const TEMPLATES_DIR = path.join(__dirname, 'templates');
20
20
 
21
21
  /** Bumped with the plugin, so a release is never served a stale script. */
22
- const ASSET_V = '1.4.4';
22
+ const ASSET_V = '1.6.0';
23
23
 
24
24
  /**
25
25
  * Above this many posts a listing pages on the server instead: the browser
@@ -24,7 +24,7 @@ test('interpolate: escaped by default, raw on request, dates formatted, comments
24
24
  test('the layouts on offer: shipped ones first, then any blog-post-* / blog-card-* block', () => {
25
25
  const names = ['blog-post-long-read', 'blog-card-poster', 'blog-card', 'cta-banner', 'blog-post-classic', 'blog-post-Bad'];
26
26
  assert.deepEqual(postLayouts(names).map(l => l.id),
27
- ['blog-post-classic', 'blog-post-feature', 'blog-post-minimal', 'blog-post-sidebar', 'blog-post-long-read']);
27
+ ['blog-post-classic', 'blog-post-feature', 'blog-post-minimal', 'blog-post-sidebar', 'blog-post-split', 'blog-post-essay', 'blog-post-long-read']);
28
28
  assert.equal(postLayouts(names).at(-1).label, 'Long read');
29
29
  const displays = listDisplays(names);
30
30
  assert.deepEqual(displays.map(d => d.id), ['cards', 'list', 'magazine', 'compact', 'blog-card-poster']);
@@ -0,0 +1,57 @@
1
+ import {test} from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {addNavLink, blogItem, hasLink, removeNavLink, withoutLink} from '../lib/nav-link.js';
4
+
5
+ function fakeMenus(items, locations = {navbar: 'main'}) {
6
+ const store = {main: {slug: 'main', name: 'Main', items}};
7
+ return {
8
+ store,
9
+ getLocations: async () => locations,
10
+ getMenu: async (slug) => store[slug] ? structuredClone(store[slug]) : null,
11
+ updateMenu: async (slug, input) => { store[slug] = structuredClone(input); return store[slug]; }
12
+ };
13
+ }
14
+
15
+ test('hasLink finds the blog at any depth, trailing slash or not', () => {
16
+ assert.equal(hasLink([{text: 'Home', url: '/'}], '/blog'), false);
17
+ assert.equal(hasLink([{text: 'More', url: '#', items: [{text: 'News', url: '/blog/'}]}], '/blog'), true);
18
+ assert.equal(hasLink([{type: 'separator'}, {type: 'spacer'}], '/blog'), false);
19
+ });
20
+
21
+ test('the item takes an icon only when the menu mostly has them', () => {
22
+ assert.deepEqual(blogItem([{text: 'Home', url: '/', icon: 'home'}, {text: 'About', url: '/about', icon: 'info'}], '/blog/'),
23
+ {text: 'Blog', url: '/blog', icon: 'file-text'});
24
+ assert.deepEqual(blogItem([{text: 'Home', url: '/'}, {text: 'About', url: '/about', icon: 'info'}, {type: 'spacer'}, {text: 'X', url: '/x'}], '/blog'),
25
+ {text: 'Blog', url: '/blog'});
26
+ assert.deepEqual(blogItem([], '/blog'), {text: 'Blog', url: '/blog'});
27
+ });
28
+
29
+ test('enabling adds the link once, and disabling takes away only what it added', async () => {
30
+ const menus = fakeMenus([{text: 'Home', url: '/', icon: 'home'}]);
31
+ const added = await addNavLink('/blog', menus);
32
+ assert.deepEqual(added, {menu: 'main', url: '/blog'});
33
+ assert.deepEqual(menus.store.main.items.map(i => i.url), ['/', '/blog']);
34
+ assert.equal(menus.store.main.name, 'Main', 'the rest of the menu is kept');
35
+
36
+ assert.equal(await addNavLink('/blog', menus), null, 'a second enable adds nothing');
37
+ assert.equal(menus.store.main.items.length, 2);
38
+
39
+ // The site renamed it in the Menus editor, which dropped nothing we rely on.
40
+ menus.store.main.items[1].text = 'Journal';
41
+ assert.equal(await removeNavLink(added, menus), true);
42
+ assert.deepEqual(menus.store.main.items.map(i => i.url), ['/']);
43
+ });
44
+
45
+ test('a link the site had already is neither duplicated nor removed', async () => {
46
+ const menus = fakeMenus([{text: 'Home', url: '/'}, {text: 'News', url: '/blog'}]);
47
+ const added = await addNavLink('/blog', menus);
48
+ assert.equal(added, null);
49
+ assert.equal(await removeNavLink(added, menus), false);
50
+ assert.equal(menus.store.main.items.length, 2);
51
+ });
52
+
53
+ test('no navbar menu mapped: nothing happens', async () => {
54
+ const menus = fakeMenus([], {});
55
+ assert.equal(await addNavLink('/blog', menus), null);
56
+ assert.deepEqual(withoutLink([{text: 'A', url: '/a'}], '/blog'), [{text: 'A', url: '/a'}]);
57
+ });
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "note": "Written by `make sync-free-tier` in dcms-marketplace. Do not edit these plugins here - edit them there and sync.",
3
- "source": "dcms-marketplace@6f9dbf3",
3
+ "source": "dcms-marketplace@cb07e46",
4
4
  "plugins": {
5
5
  "blog": {
6
- "version": "1.4.4",
6
+ "version": "1.6.0",
7
7
  "files": {
8
- "CLAUDE.md": "69087729ee22dddef5868e14fd6dd4570bb5387917020de873948785e6919756",
9
- "admin/css/index.css": "2f39febfe02491f6db0e32b766723c3a2950a6a1139a108b2ec756d395472f30",
8
+ "CLAUDE.md": "155f6e6e09531b8ab87252f942c1c75121a6c10abdebc4316fa7da44edb42642",
9
+ "admin/css/index.css": "6142a6b756e1218157842528830b1e84f592f0b84b7d5ce6449db421b6f4b039",
10
10
  "admin/templates/blog.html": "a6054c1febe764bb1edf5c9e466d44a9080d9c83ba5486437b7a8244acf5ba54",
11
11
  "admin/templates/categories.html": "ab7d6fc2fedd48876c585bc216a1532b3bdd78c946dee976c1171bfe4664feb6",
12
12
  "admin/templates/comments.html": "095ff03bd5e1ed9ac75edf1ceb9fbe7b2907a1e35b261672da51e00742c2814a",
13
- "admin/templates/post-editor.html": "c5292ecac2bfe4994ac62b4bf67631b7959dfa068cc3c1332d2268d870d0b467",
13
+ "admin/templates/post-editor.html": "f826089f040a13188087721eaa5324707b2f7d491bad41610682242d5e86cec6",
14
14
  "admin/templates/settings.html": "e0730cd57435e592034328a699198d5e1326084767156bfc2289852f5f1a45af",
15
- "admin/views/blog.js": "52bf73bfbc2d8736c1217d89dec1e69649f4b2f5334b7da1abf33bb1981dc921",
16
- "admin/views/categories.js": "dade241e8d70d101366c5c8e9eed721f7b39ffda65025a272ae5679660f0f50c",
17
- "admin/views/comments.js": "f5055e7b8e0c3fd8f71b2a8aeeb47c68fab5ff7d24ffb5d9e052eedfc1da1df2",
18
- "admin/views/kit.js": "c7b475fe5920b6429a03976a1463a218751e0ed2e42135920e4c3b1ebf47e79f",
19
- "admin/views/post-editor.js": "97ea927c539419940fccf4a78fc3098e00beca3454a56ef432fa94b7f3cacb81",
15
+ "admin/views/blog.js": "3fc84f7ea9fce352f150771b4d1f775965592a8690ad7fd1110a3a4e72c49c74",
16
+ "admin/views/categories.js": "75140feb2b382ba0ab4a52c30007845bf576883c99577fc0965783adb8808f6b",
17
+ "admin/views/comments.js": "7c93f964560b5b02df270bd17526b8f749cc7c8871a9e863bb84e9cbfbc4da0e",
18
+ "admin/views/kit.js": "e7a787836090f622d7108f2e31a041bfedd0aa6ffb9c9d0b341daa2640e13e2e",
19
+ "admin/views/post-editor.js": "6bb0867d3b57d817497115e5c56479332f2a5324ab99aab2fdabc714c888c543",
20
20
  "admin/views/settings-cog.js": "307efc93835863dd8511f8fb17a224cf45a77532c90b854a86d2cd1d4e5f9f18",
21
21
  "admin/views/settings.js": "2b7f775e37e034dee4ae3dfadea5e446c2e526bed8a066ef3ca2e777146ada23",
22
22
  "blocks/blog-card-compact.css": "eb8e8ae9a6461c362bc021ea0396c6b4481047b6b36a50eeb59e47375e49be75",
@@ -27,23 +27,28 @@
27
27
  "blocks/blog-card.html": "cf676e8e9f676ea8b49637cb54a7d6fcd6f3395af8f4a8833a65ba78b35efc76",
28
28
  "blocks/blog-post-classic.css": "9acac617847b5a10dabd5efccb601572e2751f792c4038475325f11e24142584",
29
29
  "blocks/blog-post-classic.html": "1fab77c7aeb67fe72b73c4db42ff4c3a66466c041104639f4898c390c665fc38",
30
+ "blocks/blog-post-essay.css": "d1842443927ea7c93772a06ade69fc834099e6820d99767e0aab8e80981a3784",
31
+ "blocks/blog-post-essay.html": "f699fb6399d8845d762dfde4dd6f318351427d412363b786105ddc9f3a3af2c1",
30
32
  "blocks/blog-post-feature.css": "7d8a242b627f3826df21d5470e643b386967122ea3585e69f96cce9ffc2452e9",
31
33
  "blocks/blog-post-feature.html": "c0408538cc269e4f2873f9c9c44a000347e4d5cafe3ecb024f62112ea711a9ab",
32
34
  "blocks/blog-post-minimal.css": "2323b84a7d0a99e1c85c30faa18bb2ee16763ff08cfe87f181ffadf8cff84dd0",
33
35
  "blocks/blog-post-minimal.html": "3a04925980018a13038ebf4bcfecc4f69284c1bac087631ddc0f66e0e95e07fe",
34
36
  "blocks/blog-post-sidebar.css": "3c66109fa48ca448605eb5acd0c998f43649fb3747813b4a811992ec89886007",
35
37
  "blocks/blog-post-sidebar.html": "d510a9e272e9415941064b4567def47a179b88825e29c6dd340540870ab04a61",
38
+ "blocks/blog-post-split.css": "61ae29850d6c324ffbfc2110a917d5980ad17b0635870656ccd885d20ac9ec86",
39
+ "blocks/blog-post-split.html": "c09a15c537c0502d5200dc8f3137c4f56394b0656557a88d23d3326c7238c249",
36
40
  "collections/categories/schema.json": "0728e522ed8e3c623fd45e137cd03e0960aa5158e70613ec8628c8b3928e0929",
37
41
  "collections/comments/schema.json": "29cbd289cbfcfc4158c9357a218db4ce06eddc3370bfbf9afb7d7f52065a849e",
38
42
  "collections/posts/schema.json": "19a5a770d3eefea73f4117454bd1312a7a7e3d727cfcdfde141139fbc81cabd7",
39
43
  "config.js": "ee67396262762f5c31269d258affd75f7392a3e364f72f7792c2f1cdafd94bba",
40
- "lib/layouts.js": "e55dfd79227a36e190d648dcbd3c31de538dafed9b5ca68844c98ef79b6c2509",
44
+ "lib/layouts.js": "5b3c22cfdd537777418d3d22ce86ba522576c0101e7d51ea0db9b2667caf1f92",
45
+ "lib/nav-link.js": "ab68e4f5d86665834d95f845bb9755ee3a7019089be3452571054f4a49befbbb",
41
46
  "lib/page.js": "1fb9a01c3080e8ecf65c19b1273b23d550a131fe30079a493a9f5b1c90b720ae",
42
47
  "lib/render.js": "0e97c6a950bc962c8d8ea4b2f21d06cb73fbcdc738d8942d6935d01309a29b1d",
43
48
  "lib/samples.js": "e8cdb77ce22e43937707f6854d3a5cb722437dc584e22ac7b24480bef686cf85",
44
- "plugin.js": "32e223ff46bfc72a4db6904425ef9488538a901d73c67be41a0fc0cc037e457b",
45
- "plugin.json": "16104f855c0afa2912d5a96465d6388e4090d56fabbf566401942d8160ea2d1b",
46
- "plugin.public.js": "053bbc0322ba205640fdbb834340883d0698600e7a637c8165ecac9d49c8d040",
49
+ "plugin.js": "9e49e3b1d4844cf5ec33abf350ca9cd38e8efa4b93be2c628842a923033350cf",
50
+ "plugin.json": "20603ae07a6dade8f6fef14539e3b629c7c0c07dea8322834a4827f86dcf204e",
51
+ "plugin.public.js": "c338fff588ab7bd02baf1bc6d5a729a0f0d2ecfa90c087cadfd792ba4d3fd9c2",
47
52
  "public/blog.css": "fd733e6b624190da1e70dc1b6613e2376efd7f7570a28fab311d81006d03d960",
48
53
  "public/blog.js": "d01c72a9834dcc7c5241923371f2b164ac30d20ac9f1a5c5dc5b71fa3cb09f08",
49
54
  "public/samples/aurora.svg": "feb29e2b46465913fc29de9eda221b00b1541f1fa3ea7120f0c1d1cd11a9a4f6",
@@ -56,7 +61,8 @@
56
61
  "roles/blog-editor.json": "bea0b673cf9e3da04c5882d51dd33bdcaf035b047c1cfe87624a9f16e6514ffc",
57
62
  "templates/index.html": "3c2cf8a4ffde8599d451f6d00b736aeba42fd073b726ada3b0ffe4d56e1ef0f4",
58
63
  "templates/post.html": "28e9f06071115ecc4fbba007b35782732bf12ef72528525893d424e3c4f0313f",
59
- "tests/layouts.test.js": "70e0e462985d14618838c3897ffff7c453b711b64c6fc9277023268b0755377c",
64
+ "tests/layouts.test.js": "f6dc1ac17b1d4841accd673ab34cc127aa2ccdbca453ef1cee8dbb7a637669a6",
65
+ "tests/nav-link.test.js": "d25a2ec7948df4b042c402e8c5f2cacb2be4743edba48d497a6b67a0ca1fdd5a",
60
66
  "tests/public.test.js": "ef04ba271d03cee86adac332c6834bb51102b20d892d5f84d30eb04507605de5"
61
67
  }
62
68
  },
@@ -1,2 +1,2 @@
1
- import{compareValues as ut,sortEntries as F}from"/public/js/collection-sort.mjs";import{serialise as T}from"/public/js/collection-export.mjs";import{parseQuery as ft}from"/public/js/collection-query.mjs";(function(){"use strict";const H='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',P=document.createElement("style");P.textContent=["[data-ctx], [data-collection-table] { cursor: "+H+"; }","[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-hover-bg, rgba(128,128,128,.14)); }",".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-input-bg, rgba(128,128,128,.08)); 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-hover-bg, rgba(128,128,128,.14)); 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); }",".dm-ctx-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem;border-bottom: 1px solid var(--dm-border, #2a2a40); margin-bottom: .25rem; }",".dm-ctx-menu button.dm-ctx-back { width: auto; padding: .3rem .5rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-group-heading { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); padding: .5rem 0 .25rem; grid-column: 1 / -1; }"].join(`
2
- `),document.head.appendChild(P);function g(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,o)=>({id:String(o),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 $(t,e){if(e.field==="*"){const n=String(e.value).toLowerCase();return Object.values(t.data||{}).some(d=>String(d??"").toLowerCase().includes(n))}const o=String(t.data?.[e.field]??"");switch(e.op){case"eq":return o===String(e.value);case"ne":return o!==String(e.value);case"contains":return o.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const n=ut(o,e.value);return e.op==="gt"?n>0:n<0}default:return!0}}function p(t){const e=t._dmCtxFilters||[],o=g(t);if(!o)return;const n=t._dmCtxSort||null,d=F(o.entries.filter(a=>e.every(s=>$(a,s))),n),l=new Set(d.map(a=>a.id));if(t._dmTable)t._dmTable.render(d.map(a=>a.data));else if(t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems)t._dmCtx.instance.setItems(t._dmCtx.items.filter(a=>l.has(String(a._entryId))));else if(t._dmCtxSlides)Y(t,l);else{const a=Array.from(t.querySelectorAll("[data-entry-id]"));a.forEach(h=>h.classList.toggle("dm-ctx-hidden",!l.has(h.dataset.entryId)));const s=new Map(a.map(h=>[h.dataset.entryId,h])),u=a.length?a[0].parentNode:null;n&&u&&d.forEach(h=>{const x=s.get(h.id);x&&x.parentNode===u&&u.appendChild(x)}),t.querySelectorAll(".dm-ctx-group-heading").forEach(h=>h.remove());const c=t._dmCtxGroup;if(c&&u){let h=null;d.forEach(x=>{const b=String(x.data?.[c]??"")||"-";if(b===h)return;h=b;const i=document.createElement("div");i.className="dm-ctx-group-heading",i.textContent=b;const r=s.get(x.id);r&&u.insertBefore(i,r)})}}tt(t,e),et(t,d.length,e.length),M()}const R="dmf";function A(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function V(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function J(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return JSON.parse(decodeURIComponent(escape(atob(e))))}function M(){const t={};A().forEach((n,d)=>{const l={};n._dmCtxFilters&&n._dmCtxFilters.length&&(l.f=n._dmCtxFilters),n._dmCtxSort&&(l.s=n._dmCtxSort),n._dmCtxGroup&&(l.g=n._dmCtxGroup),Object.keys(l).length&&(t[d]=l)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(n=>n&&!n.startsWith(R+"="));Object.keys(t).length&&e.push(R+"="+V(t));const o=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",o)}catch{}}function W(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return J(t[1])}catch{return null}}function N(t=0){const e=W();if(!e)return;const o=A();let n=!1;for(const[d,l]of Object.entries(e)){const a=o[Number(d)];if(!a||!l)continue;if(a.hasAttribute("data-collection-table")?!a._dmTable:a.classList.contains("dm-progression")?!a._dmCtx:a.classList.contains("carousel")?!a._dmCtxSlides:!1){n=!0;continue}const u=Array.isArray(l)?{f:l}:l||{};a._dmCtxFilters=(u.f||[]).filter(c=>c&&c.field&&c.op!=null&&"value"in c),a._dmCtxSort=u.s||null,a._dmCtxGroup=u.g||null,p(a)}n&&t<20&&setTimeout(()=>N(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(N,0)):setTimeout(N,0);function Y(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const o=document.createElement("div");o.className="carousel-track",t._dmCtxSlides.forEach(n=>{e.has(n.dataset.entryId)&&o.appendChild(n.cloneNode(!0))}),t.replaceChildren(o),o.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}function X(t,e,o){const n=URL.createObjectURL(new Blob([t],{type:o})),d=document.createElement("a");d.href=n,d.download=e,document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(n),0)}function _(t,e){const o=document.createElement("textarea");o.value=t,o.style.cssText="position:fixed;opacity:0;",document.body.appendChild(o),o.select();const n=document.execCommand("copy");o.remove(),typeof E<"u"&&E.toast&&E.toast(n?e:"Copy failed",{type:n?"success":"error"})}function Z(){return new Date().toISOString().slice(0,10)}let L=null;function K(){if(!L){const t=typeof S<"u"&&S.get?S.get("auth_token"):null;L=t?fetch("/api/auth/me",{headers:{Authorization:`Bearer ${t}`}}).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null)}return L}async function Q(t){if(!t||!t.allowed)return!1;if(t.access==="public")return!0;const e=await K();return e?[e.role,...e.additionalRoles||[]].filter(Boolean).some(n=>(t.roles||[]).includes(n)):!1}const w={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function tt(t,e){let o=t._dmCtxBar;if(!e.length){o&&(o.remove(),t._dmCtxBar=null);return}o||(o=document.createElement("div"),o.className="dm-ctx-chips",t.parentNode.insertBefore(o,t),t._dmCtxBar=o),o.replaceChildren(),e.forEach((d,l)=>{const a=document.createElement("span");a.className="dm-ctx-chip";const s=String(d.value),u=s.length>24?s.slice(0,24)+"\u2026":s;a.appendChild(document.createTextNode(d.field==="*"?`search \u201C${u}\u201D`:`${d.label||d.field} ${w[d.op]||d.op} \u201C${u}\u201D`));const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.title="Remove filter",c.addEventListener("click",()=>{t._dmCtxFilters.splice(l,1),p(t)}),a.appendChild(c),o.appendChild(a)});const n=document.createElement("button");n.type="button",n.className="dm-ctx-chip",n.style.cursor="pointer",n.textContent="Clear all",n.addEventListener("click",()=>{t._dmCtxFilters=[],p(t)}),o.appendChild(n)}function et(t,e,o){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!o)return;const n=document.createElement("div");n.className="dm-ctx-empty-note",n.textContent="No entries match the current filters.",t.appendChild(n),t._dmCtxEmptyNote=n}let C=null;function f(){C&&(C.remove(),C=null,document.removeEventListener("mousedown",O,!0),document.removeEventListener("keydown",j,!0),window.removeEventListener("scroll",D,!0))}const O=t=>{C&&!C.contains(t.target)&&f()},j=t=>{t.key==="Escape"&&f()},D=t=>{C&&!C.contains(t.target)&&f()};function m(t,e){const o=document.createElement("button");return o.type="button",o.className="dm-ctx-row",o.textContent=t,o.addEventListener("click",e),o}function v(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}function G(){const t=document.createElement("div");return t.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t}const y=(t,e=24)=>{const o=String(t);return o.length>e?o.slice(0,e)+"\u2026":o};function I(t){const e=t.getBoundingClientRect();t.style.left=Math.min(t._dmX,window.innerWidth-e.width-8)+"px",t.style.top=Math.min(t._dmY,window.innerHeight-e.height-8)+"px"}function B(t,e,o){if(t._dmPanelToken=(t._dmPanelToken||0)+1,t.replaceChildren(),o){const n=document.createElement("div");n.className="dm-ctx-panel-head";const d=document.createElement("button");d.type="button",d.className="dm-ctx-row dm-ctx-back",d.textContent="\u2039 Back",d.addEventListener("click",()=>t._dmPop()),n.appendChild(d),n.appendChild(v(o)),t.appendChild(n)}e(t),I(t)}function nt(t){const e=[];return t._dmPush=(o,n)=>{e.push({build:o,title:n}),B(t,o,n)},t._dmPop=()=>{e.pop();const o=e[e.length-1];o&&B(t,o.build,o.title)},t}function ot(t){return!t._dmTable&&!t._dmCtx?.instance?.setItems&&!t._dmCtxSlides}function dt(t,e){const o=t.entries.map(d=>d.data?.[e]).find(d=>d!==""&&d!=null),n=String(o??"");return n!==""&&!Number.isNaN(parseFloat(n))?["low \u2192 high","high \u2192 low"]:Number.isNaN(Date.parse(n))?["A \u2192 Z","Z \u2192 A"]:["oldest \u2192 newest","newest \u2192 oldest"]}function at(t,e,o){o.fields.forEach(n=>{const[d,l]=dt(o,n.name);t.appendChild(m(`${n.label||n.name} ${d}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"},p(e),f()})),t.appendChild(m(`${n.label||n.name} ${l}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"desc"},p(e),f()}))}),e._dmCtxSort&&t.appendChild(m("Clear sort",()=>{e._dmCtxSort=null,p(e),f()}))}function it(t,e,o){o.fields.forEach(n=>{t.appendChild(m(n.label||n.name,()=>{e._dmCtxGroup=n.name,(!e._dmCtxSort||e._dmCtxSort.field!==n.name)&&(e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"}),p(e),f()}))}),e._dmCtxGroup&&t.appendChild(m("Clear grouping",()=>{e._dmCtxGroup=null,p(e),f()}))}function U(t,e){const o=t._dmCtxFilters||[];return F(e.entries.filter(n=>o.every(d=>$(n,d))),t._dmCtxSort||null).map(n=>n.data)}function lt(t,e,o,n){const d=o.slug||e._dmTable?.slug||"collection";n.formats.forEach(l=>{t.appendChild(m(l.toUpperCase(),()=>{const a=T(U(e,o),o.fields,l);X(a,`${d}-${Z()}.${l}`,l==="csv"?"text/csv;charset=utf-8":"application/json"),f()}))})}function st(t,e,o,n,d){n&&d&&t.appendChild(m("This value",()=>{_(String(n.data?.[d.name]??""),"Value copied"),f()})),n&&t.appendChild(m("This row (JSON)",()=>{_(T([n.data],o.fields,"json"),"Row copied"),f()})),t.appendChild(m("Visible entries as a table",()=>{_(T(U(e,o),o.fields,"markdown"),"Table copied"),f()}))}function rt(t,e,o,n,d){const l=(i,r,k,mt)=>{(e._dmCtxFilters=e._dmCtxFilters||[]).push({field:i,label:r,op:k,value:mt}),p(e),f()};if(n&&o){const i=o.data?.[n.name]??"";t.appendChild(v(n.label||n.name)),t.appendChild(m(`Filter: = \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"eq",i))),t.appendChild(m(`Filter: \u2260 \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"ne",i)));const r=String(i);(r!==""&&!Number.isNaN(parseFloat(r))||!Number.isNaN(Date.parse(r)))&&(t.appendChild(m(`Filter: > \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"gt",i))),t.appendChild(m(`Filter: < \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"lt",i))))}else if(o){t.appendChild(v("Filter by"));for(const i of d.fields){const r=o.data?.[i.name];r==null||String(r)===""||t.appendChild(m(`${i.label||i.name}: \u201C${y(r,20)}\u201D`,()=>l(i.name,i.label,"eq",r)))}}const a=document.createElement("div");a.style.padding=".35rem .6rem";const s=document.createElement("input");s.type="text",s.className="dm-ctx-input",s.placeholder="Search\u2026 or name: value",s.addEventListener("mousedown",i=>i.stopPropagation()),s.addEventListener("keydown",i=>{if(i.key!=="Enter"||!s.value.trim())return;const r=ft(s.value,d.fields);r?l(r.field,r.label,r.op,r.value):l("*","search","contains",s.value.trim())}),a.appendChild(s),t.appendChild(a),t.appendChild(G()),t.appendChild(v("This display")),t.appendChild(m("Sort\u2026",()=>t._dmPush(i=>at(i,e,d),"Sort"))),ot(e)&&t.appendChild(m("Group by\u2026",()=>t._dmPush(i=>it(i,e,d),"Group by"))),t.appendChild(m("Copy\u2026",()=>t._dmPush(i=>st(i,e,d,o,n),"Copy")));const u=m("Print",()=>{e.setAttribute("data-dm-print",""),document.body.classList.add("dm-ctx-printing"),f();const i=()=>{e.removeAttribute("data-dm-print"),document.body.classList.remove("dm-ctx-printing"),window.removeEventListener("afterprint",i)};window.addEventListener("afterprint",i),window.print()});t.appendChild(u),t.appendChild(m("Copy link to this view",()=>{M(),_(location.href,"Link copied"),f()}));const c=d.export||e._dmTable?.exportCfg,h=t._dmPanelToken;Q(c).then(i=>{!i||C!==t||t._dmPanelToken!==h||(t.insertBefore(m("Export\u2026",()=>t._dmPush(r=>lt(r,e,d,c),"Export")),u),I(t))});const x=(e._dmCtxFilters||[]).length;if(x||e._dmCtxSort||e._dmCtxGroup){const i=[];x&&i.push(`${x} filter${x===1?"":"s"}`),e._dmCtxSort&&i.push("sort"),e._dmCtxGroup&&i.push("grouping"),t.appendChild(G()),t.appendChild(m(`Clear all (${i.join(", ")})`,()=>{e._dmCtxFilters=[],e._dmCtxSort=null,e._dmCtxGroup=null,p(e),f()}))}}function z(t,e,o,n,d){f();const l=g(o);if(!l)return;const a=nt(document.createElement("div"));a.className="dm-ctx-menu",a._dmX=t,a._dmY=e,C=a,document.body.appendChild(a),a._dmPush(s=>rt(s,o,n,d,l),null),document.addEventListener("mousedown",O,!0),document.addEventListener("keydown",j,!0),window.addEventListener("scroll",D,!0),setTimeout(()=>{const s=a.querySelector(".dm-ctx-input");s&&s.focus()},0)}function q(t,e,o){if(t._dmTable){const s=e.closest("tbody tr");if(!s)return{entry:null,field:null};const u=e.closest("td"),c=s.querySelectorAll("td"),h=Array.from(c).map(i=>i.textContent.trim()),x=o.entries.find(i=>o.fields.every((r,k)=>String(i.data?.[r.name]??"").trim()===(h[k]??"").trim()))||null,b=u&&o.fields[u.cellIndex]||null;return{entry:x,field:b}}const n=e.closest("[data-entry-id]");if(!n)return{entry:null,field:null};const d=o.entries.find(s=>s.id===n.dataset.entryId)||null;if(!d)return{entry:null,field:null};const l=(e.textContent||"").trim().slice(0,80);let a=null;if(l){const s=o.fields.filter(u=>{const c=String(d.data?.[u.name]??"");return c&&(c.includes(l)||l.includes(c))});s.length===1&&(a=s[0])}return{entry:d,field:a}}function ct(t){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}ct(()=>{if(typeof Domma>"u"||!Domma.elements?.contextMenu){document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const o=g(e);if(!o||!o.fields?.length)return;t.preventDefault();const{entry:n,field:d}=q(e,t.target,o);z(t.clientX,t.clientY,e,n,d)});return}Domma.elements.contextMenu("[data-ctx], [data-collection-table]",{exclusive:!0,enabled:t=>{const e=g(t);return!!(e&&e.fields?.length)},render:t=>{const e=t.container,o=g(e),{entry:n,field:d}=q(e,t.event.target,o);z(t.x,t.y,e,n,d)}})})})();
1
+ import{compareValues as mt,sortEntries as H}from"/public/js/collection-sort.mjs";import{serialise as T}from"/public/js/collection-export.mjs";import{parseQuery as ut}from"/public/js/collection-query.mjs";(function(){"use strict";const P=document.createElement("style");P.textContent=["[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-hover-bg, rgba(128,128,128,.14)); }",".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-input-bg, rgba(128,128,128,.08)); 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-hover-bg, rgba(128,128,128,.14)); 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); }",".dm-ctx-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem;border-bottom: 1px solid var(--dm-border, #2a2a40); margin-bottom: .25rem; }",".dm-ctx-menu button.dm-ctx-back { width: auto; padding: .3rem .5rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-group-heading { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); padding: .5rem 0 .25rem; grid-column: 1 / -1; }"].join(`
2
+ `),document.head.appendChild(P);function g(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,o)=>({id:String(o),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 $(t,e){if(e.field==="*"){const n=String(e.value).toLowerCase();return Object.values(t.data||{}).some(d=>String(d??"").toLowerCase().includes(n))}const o=String(t.data?.[e.field]??"");switch(e.op){case"eq":return o===String(e.value);case"ne":return o!==String(e.value);case"contains":return o.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const n=mt(o,e.value);return e.op==="gt"?n>0:n<0}default:return!0}}function p(t){const e=t._dmCtxFilters||[],o=g(t);if(!o)return;const n=t._dmCtxSort||null,d=H(o.entries.filter(a=>e.every(r=>$(a,r))),n),l=new Set(d.map(a=>a.id));if(t._dmTable)t._dmTable.render(d.map(a=>a.data));else if(t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems)t._dmCtx.instance.setItems(t._dmCtx.items.filter(a=>l.has(String(a._entryId))));else if(t._dmCtxSlides)W(t,l);else{const a=Array.from(t.querySelectorAll("[data-entry-id]"));a.forEach(h=>h.classList.toggle("dm-ctx-hidden",!l.has(h.dataset.entryId)));const r=new Map(a.map(h=>[h.dataset.entryId,h])),u=a.length?a[0].parentNode:null;n&&u&&d.forEach(h=>{const x=r.get(h.id);x&&x.parentNode===u&&u.appendChild(x)}),t.querySelectorAll(".dm-ctx-group-heading").forEach(h=>h.remove());const c=t._dmCtxGroup;if(c&&u){let h=null;d.forEach(x=>{const b=String(x.data?.[c]??"")||"-";if(b===h)return;h=b;const i=document.createElement("div");i.className="dm-ctx-group-heading",i.textContent=b;const s=r.get(x.id);s&&u.insertBefore(i,s)})}}w(t,e),tt(t,d.length,e.length),D()}const A="dmf";function M(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function U(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function J(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return JSON.parse(decodeURIComponent(escape(atob(e))))}function D(){const t={};M().forEach((n,d)=>{const l={};n._dmCtxFilters&&n._dmCtxFilters.length&&(l.f=n._dmCtxFilters),n._dmCtxSort&&(l.s=n._dmCtxSort),n._dmCtxGroup&&(l.g=n._dmCtxGroup),Object.keys(l).length&&(t[d]=l)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(n=>n&&!n.startsWith(A+"="));Object.keys(t).length&&e.push(A+"="+U(t));const o=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",o)}catch{}}function V(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return J(t[1])}catch{return null}}function N(t=0){const e=V();if(!e)return;const o=M();let n=!1;for(const[d,l]of Object.entries(e)){const a=o[Number(d)];if(!a||!l)continue;if(a.hasAttribute("data-collection-table")?!a._dmTable:a.classList.contains("dm-progression")?!a._dmCtx:a.classList.contains("carousel")?!a._dmCtxSlides:!1){n=!0;continue}const u=Array.isArray(l)?{f:l}:l||{};a._dmCtxFilters=(u.f||[]).filter(c=>c&&c.field&&c.op!=null&&"value"in c),a._dmCtxSort=u.s||null,a._dmCtxGroup=u.g||null,p(a)}n&&t<20&&setTimeout(()=>N(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(N,0)):setTimeout(N,0);function W(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const o=document.createElement("div");o.className="carousel-track",t._dmCtxSlides.forEach(n=>{e.has(n.dataset.entryId)&&o.appendChild(n.cloneNode(!0))}),t.replaceChildren(o),o.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}function Y(t,e,o){const n=URL.createObjectURL(new Blob([t],{type:o})),d=document.createElement("a");d.href=n,d.download=e,document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(n),0)}function _(t,e){const o=document.createElement("textarea");o.value=t,o.style.cssText="position:fixed;opacity:0;",document.body.appendChild(o),o.select();const n=document.execCommand("copy");o.remove(),typeof E<"u"&&E.toast&&E.toast(n?e:"Copy failed",{type:n?"success":"error"})}function X(){return new Date().toISOString().slice(0,10)}let L=null;function Z(){if(!L){const t=typeof S<"u"&&S.get?S.get("auth_token"):null;L=t?fetch("/api/auth/me",{headers:{Authorization:`Bearer ${t}`}}).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null)}return L}async function K(t){if(!t||!t.allowed)return!1;if(t.access==="public")return!0;const e=await Z();return e?[e.role,...e.additionalRoles||[]].filter(Boolean).some(n=>(t.roles||[]).includes(n)):!1}const Q={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function w(t,e){let o=t._dmCtxBar;if(!e.length){o&&(o.remove(),t._dmCtxBar=null);return}o||(o=document.createElement("div"),o.className="dm-ctx-chips",t.parentNode.insertBefore(o,t),t._dmCtxBar=o),o.replaceChildren(),e.forEach((d,l)=>{const a=document.createElement("span");a.className="dm-ctx-chip";const r=String(d.value),u=r.length>24?r.slice(0,24)+"\u2026":r;a.appendChild(document.createTextNode(d.field==="*"?`search \u201C${u}\u201D`:`${d.label||d.field} ${Q[d.op]||d.op} \u201C${u}\u201D`));const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.title="Remove filter",c.addEventListener("click",()=>{t._dmCtxFilters.splice(l,1),p(t)}),a.appendChild(c),o.appendChild(a)});const n=document.createElement("button");n.type="button",n.className="dm-ctx-chip",n.style.cursor="pointer",n.textContent="Clear all",n.addEventListener("click",()=>{t._dmCtxFilters=[],p(t)}),o.appendChild(n)}function tt(t,e,o){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!o)return;const n=document.createElement("div");n.className="dm-ctx-empty-note",n.textContent="No entries match the current filters.",t.appendChild(n),t._dmCtxEmptyNote=n}let C=null;function f(){C&&(C.remove(),C=null,document.removeEventListener("mousedown",O,!0),document.removeEventListener("keydown",j,!0),window.removeEventListener("scroll",G,!0))}const O=t=>{C&&!C.contains(t.target)&&f()},j=t=>{t.key==="Escape"&&f()},G=t=>{C&&!C.contains(t.target)&&f()};function m(t,e){const o=document.createElement("button");return o.type="button",o.className="dm-ctx-row",o.textContent=t,o.addEventListener("click",e),o}function v(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}function I(){const t=document.createElement("div");return t.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t}const y=(t,e=24)=>{const o=String(t);return o.length>e?o.slice(0,e)+"\u2026":o};function R(t){const e=t.getBoundingClientRect();t.style.left=Math.min(t._dmX,window.innerWidth-e.width-8)+"px",t.style.top=Math.min(t._dmY,window.innerHeight-e.height-8)+"px"}function B(t,e,o){if(t._dmPanelToken=(t._dmPanelToken||0)+1,t.replaceChildren(),o){const n=document.createElement("div");n.className="dm-ctx-panel-head";const d=document.createElement("button");d.type="button",d.className="dm-ctx-row dm-ctx-back",d.textContent="\u2039 Back",d.addEventListener("click",()=>t._dmPop()),n.appendChild(d),n.appendChild(v(o)),t.appendChild(n)}e(t),R(t)}function et(t){const e=[];return t._dmPush=(o,n)=>{e.push({build:o,title:n}),B(t,o,n)},t._dmPop=()=>{e.pop();const o=e[e.length-1];o&&B(t,o.build,o.title)},t}function nt(t){return!t._dmTable&&!t._dmCtx?.instance?.setItems&&!t._dmCtxSlides}function ot(t,e){const o=t.entries.map(d=>d.data?.[e]).find(d=>d!==""&&d!=null),n=String(o??"");return n!==""&&!Number.isNaN(parseFloat(n))?["low \u2192 high","high \u2192 low"]:Number.isNaN(Date.parse(n))?["A \u2192 Z","Z \u2192 A"]:["oldest \u2192 newest","newest \u2192 oldest"]}function dt(t,e,o){o.fields.forEach(n=>{const[d,l]=ot(o,n.name);t.appendChild(m(`${n.label||n.name} ${d}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"},p(e),f()})),t.appendChild(m(`${n.label||n.name} ${l}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"desc"},p(e),f()}))}),e._dmCtxSort&&t.appendChild(m("Clear sort",()=>{e._dmCtxSort=null,p(e),f()}))}function at(t,e,o){o.fields.forEach(n=>{t.appendChild(m(n.label||n.name,()=>{e._dmCtxGroup=n.name,(!e._dmCtxSort||e._dmCtxSort.field!==n.name)&&(e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"}),p(e),f()}))}),e._dmCtxGroup&&t.appendChild(m("Clear grouping",()=>{e._dmCtxGroup=null,p(e),f()}))}function q(t,e){const o=t._dmCtxFilters||[];return H(e.entries.filter(n=>o.every(d=>$(n,d))),t._dmCtxSort||null).map(n=>n.data)}function it(t,e,o,n){const d=o.slug||e._dmTable?.slug||"collection";n.formats.forEach(l=>{t.appendChild(m(l.toUpperCase(),()=>{const a=T(q(e,o),o.fields,l);Y(a,`${d}-${X()}.${l}`,l==="csv"?"text/csv;charset=utf-8":"application/json"),f()}))})}function lt(t,e,o,n,d){n&&d&&t.appendChild(m("This value",()=>{_(String(n.data?.[d.name]??""),"Value copied"),f()})),n&&t.appendChild(m("This row (JSON)",()=>{_(T([n.data],o.fields,"json"),"Row copied"),f()})),t.appendChild(m("Visible entries as a table",()=>{_(T(q(e,o),o.fields,"markdown"),"Table copied"),f()}))}function rt(t,e,o,n,d){const l=(i,s,k,ct)=>{(e._dmCtxFilters=e._dmCtxFilters||[]).push({field:i,label:s,op:k,value:ct}),p(e),f()};if(n&&o){const i=o.data?.[n.name]??"";t.appendChild(v(n.label||n.name)),t.appendChild(m(`Filter: = \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"eq",i))),t.appendChild(m(`Filter: \u2260 \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"ne",i)));const s=String(i);(s!==""&&!Number.isNaN(parseFloat(s))||!Number.isNaN(Date.parse(s)))&&(t.appendChild(m(`Filter: > \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"gt",i))),t.appendChild(m(`Filter: < \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"lt",i))))}else if(o){t.appendChild(v("Filter by"));for(const i of d.fields){const s=o.data?.[i.name];s==null||String(s)===""||t.appendChild(m(`${i.label||i.name}: \u201C${y(s,20)}\u201D`,()=>l(i.name,i.label,"eq",s)))}}const a=document.createElement("div");a.style.padding=".35rem .6rem";const r=document.createElement("input");r.type="text",r.className="dm-ctx-input",r.placeholder="Search\u2026 or name: value",r.addEventListener("mousedown",i=>i.stopPropagation()),r.addEventListener("keydown",i=>{if(i.key!=="Enter"||!r.value.trim())return;const s=ut(r.value,d.fields);s?l(s.field,s.label,s.op,s.value):l("*","search","contains",r.value.trim())}),a.appendChild(r),t.appendChild(a),t.appendChild(I()),t.appendChild(v("This display")),t.appendChild(m("Sort\u2026",()=>t._dmPush(i=>dt(i,e,d),"Sort"))),nt(e)&&t.appendChild(m("Group by\u2026",()=>t._dmPush(i=>at(i,e,d),"Group by"))),t.appendChild(m("Copy\u2026",()=>t._dmPush(i=>lt(i,e,d,o,n),"Copy")));const u=m("Print",()=>{e.setAttribute("data-dm-print",""),document.body.classList.add("dm-ctx-printing"),f();const i=()=>{e.removeAttribute("data-dm-print"),document.body.classList.remove("dm-ctx-printing"),window.removeEventListener("afterprint",i)};window.addEventListener("afterprint",i),window.print()});t.appendChild(u),t.appendChild(m("Copy link to this view",()=>{D(),_(location.href,"Link copied"),f()}));const c=d.export||e._dmTable?.exportCfg,h=t._dmPanelToken;K(c).then(i=>{!i||C!==t||t._dmPanelToken!==h||(t.insertBefore(m("Export\u2026",()=>t._dmPush(s=>it(s,e,d,c),"Export")),u),R(t))});const x=(e._dmCtxFilters||[]).length;if(x||e._dmCtxSort||e._dmCtxGroup){const i=[];x&&i.push(`${x} filter${x===1?"":"s"}`),e._dmCtxSort&&i.push("sort"),e._dmCtxGroup&&i.push("grouping"),t.appendChild(I()),t.appendChild(m(`Clear all (${i.join(", ")})`,()=>{e._dmCtxFilters=[],e._dmCtxSort=null,e._dmCtxGroup=null,p(e),f()}))}}function z(t,e,o,n,d){f();const l=g(o);if(!l)return;const a=et(document.createElement("div"));a.className="dm-ctx-menu",a._dmX=t,a._dmY=e,C=a,document.body.appendChild(a),a._dmPush(r=>rt(r,o,n,d,l),null),document.addEventListener("mousedown",O,!0),document.addEventListener("keydown",j,!0),window.addEventListener("scroll",G,!0),setTimeout(()=>{const r=a.querySelector(".dm-ctx-input");r&&r.focus()},0)}function F(t,e,o){if(t._dmTable){const r=e.closest("tbody tr");if(!r)return{entry:null,field:null};const u=e.closest("td"),c=r.querySelectorAll("td"),h=Array.from(c).map(i=>i.textContent.trim()),x=o.entries.find(i=>o.fields.every((s,k)=>String(i.data?.[s.name]??"").trim()===(h[k]??"").trim()))||null,b=u&&o.fields[u.cellIndex]||null;return{entry:x,field:b}}const n=e.closest("[data-entry-id]");if(!n)return{entry:null,field:null};const d=o.entries.find(r=>r.id===n.dataset.entryId)||null;if(!d)return{entry:null,field:null};const l=(e.textContent||"").trim().slice(0,80);let a=null;if(l){const r=o.fields.filter(u=>{const c=String(d.data?.[u.name]??"");return c&&(c.includes(l)||l.includes(c))});r.length===1&&(a=r[0])}return{entry:d,field:a}}function st(t){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}st(()=>{if(typeof Domma>"u"||!Domma.elements?.contextMenu){document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const o=g(e);if(!o||!o.fields?.length)return;t.preventDefault();const{entry:n,field:d}=F(e,t.target,o);z(t.clientX,t.clientY,e,n,d)});return}Domma.elements.contextMenu("[data-ctx], [data-collection-table]",{cursor:"glyph",exclusive:!0,enabled:t=>{const e=g(t);return!!(e&&e.fields?.length)},render:t=>{const e=t.container,o=g(e),{entry:n,field:d}=F(e,t.event.target,o);z(t.x,t.y,e,n,d)}})})})();
@@ -1 +1 @@
1
- (function(){"use strict";const d=window.__CMS_CTXMENUS__;if(!Array.isArray(d)||!d.length||typeof Domma>"u"||!Domma.elements||!Domma.elements.contextMenu)return;const s=(e,t)=>{typeof E<"u"&&E.toast&&E.toast(e,{type:t||"info"})},p=".site-search-trigger, .site-search-floating-trigger";function f(e){return e&&e.closest?e.closest("[data-ctx], [data-collection-table]"):null}function i(e){if(!e||!e.closest)return null;const t=e.closest('input, textarea, [contenteditable=""], [contenteditable="true"]');return!t||t.disabled||t.readOnly||t.tagName==="INPUT"&&!/^(text|search|url|tel|email|password|number)$/i.test(t.type)?null:t}function u(e){const t=i(e);if(t&&typeof t.selectionStart=="number")return t.value.slice(t.selectionStart,t.selectionEnd);const n=window.getSelection();return n?n.toString():""}function y(){try{const e=performance.getEntriesByType("navigation")[0];return!!e&&e.type==="back_forward"}catch{return!1}}function m(e,t){const n=e.selectionStart,r=e.selectionEnd;e.value=e.value.slice(0,n)+t+e.value.slice(r);const o=n+t.length;e.setSelectionRange(o,o),e.dispatchEvent(new Event("input",{bubbles:!0}))}const l={reload:{run:()=>window.location.reload()},back:{run:()=>window.history.back(),disabled:()=>window.history.length<=1},forward:{run:()=>window.history.forward(),disabled:()=>!y()},print:{run:()=>window.print()},copy:{run:async e=>{const t=u(e);if(t)try{await navigator.clipboard.writeText(t)}catch{s("Your browser would not let the page copy","error")}},disabled:e=>!u(e)},cut:{run:async e=>{const t=i(e);if(!t)return;const n=u(e);if(n)try{await navigator.clipboard.writeText(n),m(t,"")}catch{s("Your browser would not let the page cut","error")}},disabled:e=>!i(e)||!u(e)},paste:{run:async e=>{const t=i(e);if(t)try{const n=await navigator.clipboard.readText();n&&m(t,n)}catch{s("Your browser blocks paste from a menu - use Ctrl+V","warning")}},disabled:e=>!i(e)},"site-search":{run:()=>{if(window.DommaSiteSearch&&window.DommaSiteSearch.open){window.DommaSiteSearch.open();return}const e=document.querySelector(p);if(e){e.click();return}s("Site search is not available on this site","warning")},disabled:()=>!window.DommaSiteSearch&&!document.querySelector(p)},"select-all":{run:e=>{const t=i(e);if(t){t.select();return}const n=e&&e.closest&&e.closest("[data-ctx], .page-body")||document.body,r=document.createRange();r.selectNodeContents(n);const o=window.getSelection();o.removeAllRanges(),o.addRange(r)}}};function w(e){if(!e||!e.type)return null;if(l[e.type])return l[e.type].run;switch(e.type){case"navigate":return e.url?()=>{window.location.href=e.url}:null;case"copy-link":return async()=>{try{await navigator.clipboard.writeText(window.location.href),s("Link copied","success")}catch{s("Could not copy the link","error")}};case"run-action":return e.slug?async t=>{try{const n=await fetch(`/api/actions/${encodeURIComponent(e.slug)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({entryId:t&&t.dataset?t.dataset.entryId:void 0,url:window.location.pathname})});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Done","success")}catch(n){s(`Action failed: ${n.message}`,"error")}}:null;case"mark-as-spam":return async t=>{const n=t&&t.closest?t.closest("[data-entry-id]"):null,r=n&&n.dataset?n.dataset.entryId:"",o=t&&t.closest?t.closest("[data-ctx]"):null;let c="";try{o&&o.dataset.ctx&&(c=(JSON.parse(atob(o.dataset.ctx))||{}).slug||"")}catch{}if(!r||!c){s("Right-click an entry in a collection display to mark it","error");return}try{const a=await fetch(`/api/collections/${encodeURIComponent(c)}/entries/${encodeURIComponent(r)}/spam`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"same-origin",body:JSON.stringify({field:e.field||void 0})});if(a.status===401||a.status===403){s("You do not have permission to do that","error");return}if(!a.ok)throw new Error(`HTTP ${a.status}`);s("Marked as spam","success"),n&&n.setAttribute("data-spam","true")}catch(a){s(`Could not mark as spam: ${a.message}`,"error")}};case"open-modal":return e.block?async()=>{try{const t=await fetch(`/api/blocks/${encodeURIComponent(e.block)}/render`);if(!t.ok)throw new Error(`HTTP ${t.status}`);const{html:n}=await t.json(),r=E.modal({title:e.title||"",closeOnBackdrop:!0}),o=document.createElement("div");o.innerHTML=typeof DOMPurify<"u"?DOMPurify.sanitize(n):n,r.element.appendChild(o),r.open()}catch(t){s(`Could not open: ${t.message}`,"error")}}:null;case"search":return t=>{const n=f(t);n&&n.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,clientX:0,clientY:0}))};case"filter":case"sort":case"export":return t=>{const n=f(t);n&&n.dispatchEvent(new CustomEvent("dm-ctx-request",{bubbles:!0,detail:{...e}}))};default:return null}}const b=new Set(["search","filter","sort","export"]);function g(e){return l[e]&&l[e].disabled?l[e].disabled:b.has(e)?t=>!f(t):null}function h(e){const t=[];for(const n of e||[]){const r=n.type||"item";if(r==="divider"){t.push({type:"divider"});continue}if(r==="header"){t.push({type:"header",header:n.text||""});continue}if(Array.isArray(n.submenu)&&n.submenu.length){const a=h(n.submenu);a.length&&t.push({label:n.text,icon:n.icon,danger:!!n.danger,submenu:a});continue}const o=w(n.action);if(!o)continue;const c=g(n.action&&n.action.type);t.push({label:n.text,icon:n.icon||void 0,danger:!!n.danger,shortcut:n.shortcut||void 0,...c?{disabled:c}:{},action:o})}return t.some(n=>n.label)?t:[]}for(const e of d){const t=h(e.items);if(!t.length)continue;const n=e.target||{};if(!n.selector)continue;const r=e.theme||{};try{Domma.elements.contextMenu(n.selector,{match:n.match||null,exclude:n.exclude||null,inherit:e.inherit===void 0?"append":e.inherit,priority:e.priority||0,items:t,accent:r.accent,surface:r.surface,radius:r.radius,shadow:r.shadow,opacity:r.opacity,density:r.density,transition:r.transition,ariaLabel:e.name||"Context menu"})}catch(o){console.warn(`[context-menus] Skipped "${e.slug}": ${o.message}`)}}})();
1
+ (function(){"use strict";const d=window.__CMS_CTXMENUS__;if(!Array.isArray(d)||!d.length||typeof Domma>"u"||!Domma.elements||!Domma.elements.contextMenu)return;const s=(e,t)=>{typeof E<"u"&&E.toast&&E.toast(e,{type:t||"info"})},p=".site-search-trigger, .site-search-floating-trigger";function f(e){return e&&e.closest?e.closest("[data-ctx], [data-collection-table]"):null}function i(e){if(!e||!e.closest)return null;const t=e.closest('input, textarea, [contenteditable=""], [contenteditable="true"]');return!t||t.disabled||t.readOnly||t.tagName==="INPUT"&&!/^(text|search|url|tel|email|password|number)$/i.test(t.type)?null:t}function u(e){const t=i(e);if(t&&typeof t.selectionStart=="number")return t.value.slice(t.selectionStart,t.selectionEnd);const n=window.getSelection();return n?n.toString():""}function y(){try{const e=performance.getEntriesByType("navigation")[0];return!!e&&e.type==="back_forward"}catch{return!1}}function m(e,t){const n=e.selectionStart,r=e.selectionEnd;e.value=e.value.slice(0,n)+t+e.value.slice(r);const o=n+t.length;e.setSelectionRange(o,o),e.dispatchEvent(new Event("input",{bubbles:!0}))}const l={reload:{run:()=>window.location.reload()},back:{run:()=>window.history.back(),disabled:()=>window.history.length<=1},forward:{run:()=>window.history.forward(),disabled:()=>!y()},print:{run:()=>window.print()},copy:{run:async e=>{const t=u(e);if(t)try{await navigator.clipboard.writeText(t)}catch{s("Your browser would not let the page copy","error")}},disabled:e=>!u(e)},cut:{run:async e=>{const t=i(e);if(!t)return;const n=u(e);if(n)try{await navigator.clipboard.writeText(n),m(t,"")}catch{s("Your browser would not let the page cut","error")}},disabled:e=>!i(e)||!u(e)},paste:{run:async e=>{const t=i(e);if(t)try{const n=await navigator.clipboard.readText();n&&m(t,n)}catch{s("Your browser blocks paste from a menu - use Ctrl+V","warning")}},disabled:e=>!i(e)},"site-search":{run:()=>{if(window.DommaSiteSearch&&window.DommaSiteSearch.open){window.DommaSiteSearch.open();return}const e=document.querySelector(p);if(e){e.click();return}s("Site search is not available on this site","warning")},disabled:()=>!window.DommaSiteSearch&&!document.querySelector(p)},"select-all":{run:e=>{const t=i(e);if(t){t.select();return}const n=e&&e.closest&&e.closest("[data-ctx], .page-body")||document.body,r=document.createRange();r.selectNodeContents(n);const o=window.getSelection();o.removeAllRanges(),o.addRange(r)}}};function w(e){if(!e||!e.type)return null;if(l[e.type])return l[e.type].run;switch(e.type){case"navigate":return e.url?()=>{window.location.href=e.url}:null;case"copy-link":return async()=>{try{await navigator.clipboard.writeText(window.location.href),s("Link copied","success")}catch{s("Could not copy the link","error")}};case"run-action":return e.slug?async t=>{try{const n=await fetch(`/api/actions/${encodeURIComponent(e.slug)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({entryId:t&&t.dataset?t.dataset.entryId:void 0,url:window.location.pathname})});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Done","success")}catch(n){s(`Action failed: ${n.message}`,"error")}}:null;case"mark-as-spam":return async t=>{const n=t&&t.closest?t.closest("[data-entry-id]"):null,r=n&&n.dataset?n.dataset.entryId:"",o=t&&t.closest?t.closest("[data-ctx]"):null;let c="";try{o&&o.dataset.ctx&&(c=(JSON.parse(atob(o.dataset.ctx))||{}).slug||"")}catch{}if(!r||!c){s("Right-click an entry in a collection display to mark it","error");return}try{const a=await fetch(`/api/collections/${encodeURIComponent(c)}/entries/${encodeURIComponent(r)}/spam`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"same-origin",body:JSON.stringify({field:e.field||void 0})});if(a.status===401||a.status===403){s("You do not have permission to do that","error");return}if(!a.ok)throw new Error(`HTTP ${a.status}`);s("Marked as spam","success"),n&&n.setAttribute("data-spam","true")}catch(a){s(`Could not mark as spam: ${a.message}`,"error")}};case"open-modal":return e.block?async()=>{try{const t=await fetch(`/api/blocks/${encodeURIComponent(e.block)}/render`);if(!t.ok)throw new Error(`HTTP ${t.status}`);const{html:n}=await t.json(),r=E.modal({title:e.title||"",closeOnBackdrop:!0}),o=document.createElement("div");o.innerHTML=typeof DOMPurify<"u"?DOMPurify.sanitize(n):n,r.element.appendChild(o),r.open()}catch(t){s(`Could not open: ${t.message}`,"error")}}:null;case"search":return t=>{const n=f(t);n&&n.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,clientX:0,clientY:0}))};case"filter":case"sort":case"export":return t=>{const n=f(t);n&&n.dispatchEvent(new CustomEvent("dm-ctx-request",{bubbles:!0,detail:{...e}}))};default:return null}}const b=new Set(["search","filter","sort","export"]);function g(e){return l[e]&&l[e].disabled?l[e].disabled:b.has(e)?t=>!f(t):null}function h(e){const t=[];for(const n of e||[]){const r=n.type||"item";if(r==="divider"){t.push({type:"divider"});continue}if(r==="header"){t.push({type:"header",header:n.text||""});continue}if(Array.isArray(n.submenu)&&n.submenu.length){const a=h(n.submenu);a.length&&t.push({label:n.text,icon:n.icon,danger:!!n.danger,submenu:a});continue}const o=w(n.action);if(!o)continue;const c=g(n.action&&n.action.type);t.push({label:n.text,icon:n.icon||void 0,danger:!!n.danger,shortcut:n.shortcut||void 0,...c?{disabled:c}:{},action:o})}return t.some(n=>n.label)?t:[]}for(const e of d){const t=h(e.items);if(!t.length)continue;const n=e.target||{};if(!n.selector)continue;const r=e.theme||{};try{Domma.elements.contextMenu(n.selector,{cursor:"glyph",match:n.match||null,exclude:n.exclude||null,inherit:e.inherit===void 0?"append":e.inherit,priority:e.priority||0,items:t,accent:r.accent,surface:r.surface,radius:r.radius,shadow:r.shadow,opacity:r.opacity,density:r.density,transition:r.transition,ariaLabel:e.name||"Context menu"})}catch(o){console.warn(`[context-menus] Skipped "${e.slug}": ${o.message}`)}}})();