wiki-plugin-hearth 0.1.0 → 0.2.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.
- package/client/hearth.js +4 -4
- package/client/hearth.js.map +3 -3
- package/package.json +16 -5
- package/pages/about-hearth-plugin +2 -2
package/client/hearth.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
(()=>{var i=e=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),p=e=>{let t={notice:"This page keeps to its hearth. Please do not republish it outside its home.",expires:""};for(let
|
|
1
|
+
(()=>{var i=e=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),p=e=>{let t={notice:"This page keeps to its hearth. Please do not republish it outside its home.",expires:""};for(let a of(e||"").split(/\n/)){let n=a.match(/^([A-Z]+):?\s+(.*)$/);if(!n)continue;let[,r,s]=n;r==="NOTE"&&(t.notice=s.trim()||t.notice),r==="EXPIRES"&&(t.expires=s.trim())}return t},c=(e,t=Date.now())=>{if(!e)return"";let a=Date.parse(e.replace(/Z$/,"Z"));if(Number.isNaN(a))return"";let n=Math.round((a-t)/1e3);if(n<=0)return"expired";let r=Math.floor(n/86400),s=Math.floor(n%86400/3600),o=Math.floor(n%3600/60);return r?`disappears in ${r}d ${s}h`:s?`disappears in ${s}h ${o}m`:`disappears in ${o}m`},d=e=>{let t=e[0];!t||t.dataset.hearthGuard||(t.dataset.hearthGuard="1",t.addEventListener("dblclick",a=>{e.hasClass("remote")&&(a.preventDefault(),a.stopPropagation())},!0))},l=(e,t)=>{let a=p(t.text||""),n=e.parents(".page");n.addClass("hearth-page"),d(n);let r=c(a.expires);e.html(`
|
|
2
2
|
<div class="hearth-mark" style="display:flex;gap:8px;align-items:center;padding:8px 10px;border-radius:6px;background:rgba(200,120,40,0.12);border:1px solid rgba(200,120,40,0.4);font-size:0.85em;">
|
|
3
3
|
<span aria-hidden="true" style="font-size:1.3em;">\u{1F3E0}</span>
|
|
4
|
-
<span class="hearth-notice">${i(
|
|
5
|
-
${
|
|
4
|
+
<span class="hearth-notice">${i(a.notice)}</span>
|
|
5
|
+
${r?`<span class="hearth-expiry" style="margin-left:auto;color:#a05000;font-family:ui-monospace,monospace;">${i(r)}</span>`:""}
|
|
6
6
|
</div>
|
|
7
|
-
`)},
|
|
7
|
+
`)},h=(e,t)=>{e.find(".hearth-mark").on("dblclick",()=>wiki.textEditor(e,t))};if(typeof window<"u"&&(window.plugins=window.plugins||{},window.plugins.hearth={emit:l,bind:h},!document.getElementById("hearth-style"))){let e=document.createElement("style");e.id="hearth-style",e.textContent=".page.hearth-page{box-shadow:inset 0 0 0 3px rgba(200,120,40,0.5),0 0 22px rgba(200,120,40,0.35);background:linear-gradient(rgba(200,120,40,0.05),rgba(200,120,40,0.05));}.page.hearth-page .fork-page{display:none;}",document.head.appendChild(e)}})();
|
|
8
8
|
//# sourceMappingURL=hearth.js.map
|
package/client/hearth.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/hearth.js"],
|
|
4
|
-
"sourcesContent": ["/*\n * Federated Wiki : Hearth Plugin\n *\n * A `hearth` item marks a page as keeping to its hearth \u2014 meant to stay in the\n * private space it was written in. On render it tints the whole page with a\n * warm halo and shows its own plain-text notice, so even a copy that escapes to\n * a wiki without our client still carries the words that ask it home.\n *\n * The teeth \u2014 blocking
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["escapeHtml", "s", "parse", "text", "opts", "line", "m", "cmd", "arg", "countdown", "expires", "now", "when", "secs", "d", "h", "emit", "div", "item", "cd", "bind"]
|
|
4
|
+
"sourcesContent": ["/*\n * Federated Wiki : Hearth Plugin\n *\n * A `hearth` item marks a page as keeping to its hearth \u2014 meant to stay in the\n * private space it was written in. On render it tints the whole page with a\n * warm halo and shows its own plain-text notice, so even a copy that escapes to\n * a wiki without our client still carries the words that ask it home.\n *\n * This file carries its own first teeth: the journal's fork button is hidden\n * on a hearth page, and on a non-origin copy double-click editing is blocked\n * (editing away from home would fork the page). The heavier teeth \u2014 blocking\n * flag drag-out and drop-in \u2014 live in wiki-plugin-hitchhiker's wiki-ux.js,\n * which runs farm-wide and detects this item by the `hearth-page` class this\n * file adds. See the hearth-pages spec on mouse.localhost.\n *\n * Licensed under the MIT license.\n */\n\nconst escapeHtml = s =>\n String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n\n// fedwiki-dsl: UPPERCASE first word = command, optional trailing colon.\n// NOTE line adds a custom notice; EXPIRES <iso> declares a disappearing page\n// (the reaper reads it server-side; here we just surface it).\nexport const parse = text => {\n const opts = {\n notice: 'This page keeps to its hearth. Please do not republish it outside its home.',\n expires: '',\n }\n for (const line of (text || '').split(/\\n/)) {\n const m = line.match(/^([A-Z]+):?\\s+(.*)$/)\n if (!m) continue\n const [, cmd, arg] = m\n if (cmd === 'NOTE') opts.notice = arg.trim() || opts.notice\n if (cmd === 'EXPIRES') opts.expires = arg.trim()\n }\n return opts\n}\n\n// A human, timezone-aware countdown, or '' when not expiring / already past.\nexport const countdown = (expires, now = Date.now()) => {\n if (!expires) return ''\n const when = Date.parse(expires.replace(/Z$/, 'Z'))\n if (Number.isNaN(when)) return ''\n const secs = Math.round((when - now) / 1000)\n if (secs <= 0) return 'expired'\n const d = Math.floor(secs / 86400)\n const h = Math.floor((secs % 86400) / 3600)\n const m = Math.floor((secs % 3600) / 60)\n if (d) return `disappears in ${d}d ${h}h`\n if (h) return `disappears in ${h}h ${m}m`\n return `disappears in ${m}m`\n}\n\n// The hearth's own teeth. Editing a page away from its origin implicitly\n// forks it, so on a remote page a capture-phase listener swallows dblclick\n// before any item's edit handler sees it. Remoteness is checked at event\n// time \u2014 the page element persists across refreshes, its classes change.\nexport const guardPage = $page => {\n const el = $page[0]\n if (!el || el.dataset.hearthGuard) return\n el.dataset.hearthGuard = '1'\n el.addEventListener(\n 'dblclick',\n e => {\n if ($page.hasClass('remote')) {\n e.preventDefault()\n e.stopPropagation()\n }\n },\n true,\n )\n}\n\nexport const emit = (div, item) => {\n const opts = parse(item.text || '')\n const $page = div.parents('.page')\n $page.addClass('hearth-page')\n guardPage($page)\n\n const cd = countdown(opts.expires)\n div.html(`\n <div class=\"hearth-mark\" style=\"display:flex;gap:8px;align-items:center;padding:8px 10px;border-radius:6px;background:rgba(200,120,40,0.12);border:1px solid rgba(200,120,40,0.4);font-size:0.85em;\">\n <span aria-hidden=\"true\" style=\"font-size:1.3em;\">\\u{1F3E0}</span>\n <span class=\"hearth-notice\">${escapeHtml(opts.notice)}</span>\n ${cd ? `<span class=\"hearth-expiry\" style=\"margin-left:auto;color:#a05000;font-family:ui-monospace,monospace;\">${escapeHtml(cd)}</span>` : ''}\n </div>\n `)\n}\n\nexport const bind = (div, item) => {\n div.find('.hearth-mark').on('dblclick', () => wiki.textEditor(div, item))\n}\n\nif (typeof window !== 'undefined') {\n window.plugins = window.plugins || {}\n window.plugins['hearth'] = { emit, bind }\n\n // Farm-wide halo \u2014 injected once. wiki-plugin-hitchhiker styles it further.\n if (!document.getElementById('hearth-style')) {\n const s = document.createElement('style')\n s.id = 'hearth-style'\n s.textContent =\n '.page.hearth-page{box-shadow:inset 0 0 0 3px rgba(200,120,40,0.5),0 0 22px rgba(200,120,40,0.35);' +\n 'background:linear-gradient(rgba(200,120,40,0.05),rgba(200,120,40,0.05));}' +\n '.page.hearth-page .fork-page{display:none;}'\n document.head.appendChild(s)\n }\n}\n"],
|
|
5
|
+
"mappings": "MAkBA,IAAMA,EAAaC,GACjB,OAAOA,CAAC,EAAE,QAAQ,KAAM,OAAO,EAAE,QAAQ,KAAM,MAAM,EAAE,QAAQ,KAAM,MAAM,EAAE,QAAQ,KAAM,QAAQ,EAKxFC,EAAQC,GAAQ,CAC3B,IAAMC,EAAO,CACX,OAAQ,8EACR,QAAS,EACX,EACA,QAAWC,KAASF,GAAQ,IAAI,MAAM,IAAI,EAAG,CAC3C,IAAMG,EAAID,EAAK,MAAM,qBAAqB,EAC1C,GAAI,CAACC,EAAG,SACR,GAAM,CAAC,CAAEC,EAAKC,CAAG,EAAIF,EACjBC,IAAQ,SAAQH,EAAK,OAASI,EAAI,KAAK,GAAKJ,EAAK,QACjDG,IAAQ,YAAWH,EAAK,QAAUI,EAAI,KAAK,EACjD,CACA,OAAOJ,CACT,EAGaK,EAAY,CAACC,EAASC,EAAM,KAAK,IAAI,IAAM,CACtD,GAAI,CAACD,EAAS,MAAO,GACrB,IAAME,EAAO,KAAK,MAAMF,EAAQ,QAAQ,KAAM,GAAG,CAAC,EAClD,GAAI,OAAO,MAAME,CAAI,EAAG,MAAO,GAC/B,IAAMC,EAAO,KAAK,OAAOD,EAAOD,GAAO,GAAI,EAC3C,GAAIE,GAAQ,EAAG,MAAO,UACtB,IAAMC,EAAI,KAAK,MAAMD,EAAO,KAAK,EAC3BE,EAAI,KAAK,MAAOF,EAAO,MAAS,IAAI,EACpCP,EAAI,KAAK,MAAOO,EAAO,KAAQ,EAAE,EACvC,OAAIC,EAAU,iBAAiBA,CAAC,KAAKC,CAAC,IAClCA,EAAU,iBAAiBA,CAAC,KAAKT,CAAC,IAC/B,iBAAiBA,CAAC,GAC3B,EAMaU,EAAYC,GAAS,CAChC,IAAMC,EAAKD,EAAM,CAAC,EACd,CAACC,GAAMA,EAAG,QAAQ,cACtBA,EAAG,QAAQ,YAAc,IACzBA,EAAG,iBACD,WACAC,GAAK,CACCF,EAAM,SAAS,QAAQ,IACzBE,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EAEtB,EACA,EACF,EACF,EAEaC,EAAO,CAACC,EAAKC,IAAS,CACjC,IAAMlB,EAAOF,EAAMoB,EAAK,MAAQ,EAAE,EAC5BL,EAAQI,EAAI,QAAQ,OAAO,EACjCJ,EAAM,SAAS,aAAa,EAC5BD,EAAUC,CAAK,EAEf,IAAMM,EAAKd,EAAUL,EAAK,OAAO,EACjCiB,EAAI,KAAK;AAAA;AAAA;AAAA,oCAGyBrB,EAAWI,EAAK,MAAM,CAAC;AAAA,QACnDmB,EAAK,0GAA0GvB,EAAWuB,CAAE,CAAC,UAAY,EAAE;AAAA;AAAA,GAEhJ,CACH,EAEaC,EAAO,CAACH,EAAKC,IAAS,CACjCD,EAAI,KAAK,cAAc,EAAE,GAAG,WAAY,IAAM,KAAK,WAAWA,EAAKC,CAAI,CAAC,CAC1E,EAEA,GAAI,OAAO,OAAW,MACpB,OAAO,QAAU,OAAO,SAAW,CAAC,EACpC,OAAO,QAAQ,OAAY,CAAE,KAAAF,EAAM,KAAAI,CAAK,EAGpC,CAAC,SAAS,eAAe,cAAc,GAAG,CAC5C,IAAMvB,EAAI,SAAS,cAAc,OAAO,EACxCA,EAAE,GAAK,eACPA,EAAE,YACA,wNAGF,SAAS,KAAK,YAAYA,CAAC,CAC7B",
|
|
6
|
+
"names": ["escapeHtml", "s", "parse", "text", "opts", "line", "m", "cmd", "arg", "countdown", "expires", "now", "when", "secs", "d", "h", "guardPage", "$page", "el", "e", "emit", "div", "item", "cd", "bind"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-plugin-hearth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Federated Wiki hearth item — marks a page as keeping to its hearth: a coloured halo, and (with wiki-plugin-hitchhiker) hard to fork or publish outside its home farm.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "client/hearth.js",
|
|
7
|
-
"files": [
|
|
7
|
+
"files": [
|
|
8
|
+
"client",
|
|
9
|
+
"pages",
|
|
10
|
+
"factory.json"
|
|
11
|
+
],
|
|
8
12
|
"scripts": {
|
|
9
13
|
"build": "node scripts/build.js",
|
|
10
14
|
"test": "node --test"
|
|
11
15
|
},
|
|
12
16
|
"dependencies": {},
|
|
13
|
-
"devDependencies": {
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"esbuild": "^0.25.0"
|
|
19
|
+
},
|
|
14
20
|
"author": "David Bovill",
|
|
15
21
|
"license": "MIT",
|
|
16
|
-
"repository": {
|
|
17
|
-
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-hearth.git"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-hearth/issues"
|
|
28
|
+
},
|
|
18
29
|
"homepage": "https://github.com/Hitchhikers-Guide-to-the-Galaxy/wiki-plugin-hearth#readme"
|
|
19
30
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
{
|
|
31
31
|
"type": "markdown",
|
|
32
32
|
"id": "75f3d9fb92f34d8b",
|
|
33
|
-
"text": "## The Teeth\n\
|
|
33
|
+
"text": "## The Teeth\n\nThe plugin carries the first teeth itself. On a hearth page the journal's fork button is hidden. On a copy viewed away from its origin, double-click editing is blocked — editing there would fork the page.\n\nThe heavier teeth live in the hitchhiker UX layer (`wiki-plugin-hitchhiker`), farm-wide: a capture listener blocks dragging a hearth page's flag out, and the drop handler refuses to import a hearth-marked page. Enforcement is total only where these clients run; elsewhere the mark is etiquette, friction and provenance. Soft security by design."
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
"type": "markdown",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
{
|
|
80
80
|
"type": "markdown",
|
|
81
81
|
"id": "75f3d9fb92f34d8b",
|
|
82
|
-
"text": "## The Teeth\n\
|
|
82
|
+
"text": "## The Teeth\n\nThe plugin carries the first teeth itself. On a hearth page the journal's fork button is hidden. On a copy viewed away from its origin, double-click editing is blocked — editing there would fork the page.\n\nThe heavier teeth live in the hitchhiker UX layer (`wiki-plugin-hitchhiker`), farm-wide: a capture listener blocks dragging a hearth page's flag out, and the drop handler refuses to import a hearth-marked page. Enforcement is total only where these clients run; elsewhere the mark is etiquette, friction and provenance. Soft security by design."
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"type": "markdown",
|