camox 0.72.0 → 0.75.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.
Files changed (38) hide show
  1. package/dist/core/lib/artifactRuntime.js +4 -2
  2. package/dist/features/preview/components/PageInfo.js +121 -98
  3. package/dist/features/preview/components/SelectionEditor.js +1 -1
  4. package/dist/features/renderer/RendererFrames.js +47 -41
  5. package/dist/features/routes/pageRuntime.js +15 -4
  6. package/dist/features/runtime/ClaimCard.js +192 -0
  7. package/dist/features/runtime/pageApp.js +4 -3
  8. package/dist/features/runtime/runtime.js +26 -5
  9. package/dist/features/runtime/worker.js +2 -2
  10. package/dist/features/runtime/workerRequest.d.ts +6 -0
  11. package/dist/features/runtime/workerRequest.js +13 -1
  12. package/dist/features/runtime/workerRuntime.d.ts +2 -2
  13. package/dist/features/runtime/workerRuntime.js +2 -2
  14. package/dist/features/studio/components/AgentSidebar.js +7 -7
  15. package/dist/features/studio/components/ChatWidget.js +142 -106
  16. package/dist/features/studio/components/ContentFindings.js +127 -230
  17. package/dist/features/studio/components/DeleteDraftDialog.js +112 -0
  18. package/dist/features/studio/components/DraftBar.js +98 -53
  19. package/dist/features/studio/components/DraftsControl.js +203 -190
  20. package/dist/features/studio/components/ReviewDraftDialog.js +670 -0
  21. package/dist/features/studio/components/{Patrol.js → Specialists.js} +291 -330
  22. package/dist/features/studio/components/Squareling.js +5 -5
  23. package/dist/features/studio/components/ThreadComposer.js +106 -127
  24. package/dist/features/studio/components/ThreadMessages.js +246 -71
  25. package/dist/features/studio/findingSentence.js +14 -12
  26. package/dist/features/studio/fixMessage.js +23 -0
  27. package/dist/features/studio/reportSentence.js +1 -1
  28. package/dist/features/studio/{patrollerBio.js → specialistBio.js} +8 -5
  29. package/dist/features/studio/studioStore.js +29 -15
  30. package/dist/features/studio/workingSentence.js +1 -1
  31. package/dist/lib/api-client.js +5 -1
  32. package/dist/lib/queries.js +5 -5
  33. package/dist/lib/studio-reads.d.ts +1 -1
  34. package/dist/lib/studio-reads.js +5 -5
  35. package/dist/package.js +1 -1
  36. package/dist/studio.css +1 -1
  37. package/package.json +4 -4
  38. package/dist/features/studio/components/ReviewDraftSheet.js +0 -1587
@@ -23,7 +23,9 @@
23
23
  * document's `height` whenever it changes.
24
24
  * - runtime → page: `navigate` with an `href`. The host acts only with transient user activation.
25
25
  * - host → frame: `capture`, from the look document's hosts only: the runtime enters capture mode,
26
- * where real time never advances and the Timeline only moves when seeked. Only in capture mode
26
+ * where real time never advances and the Timeline only moves when seeked. Until the first seek it
27
+ * shows its poster's frame, as under reduced motion, so a capture of the page around it shows the
28
+ * frame that stands for the artifact. Only in capture mode
27
29
  * does it answer `timeline` (→ `timeline` with the same `id` and the declared `timeline`, or
28
30
  * `null` without one) and `seek` with `t` in ms (→ `seeked` with the same `id`, once the frame at
29
31
  * that time painted, and an `error` when `render` threw) and `guardrail` (→ `guardrail` with the
@@ -51,7 +53,7 @@
51
53
  * `plugins/minifyArtifactRuntime.ts`), so it must stay one template literal without
52
54
  * interpolations or backslashes.
53
55
  */
54
- const ARTIFACT_RUNTIME = "(()=>{\"use strict\";let e=window.parent,t=null,n=null,r,i,a=new Set,o=location.hash.slice(1),s=()=>{if(i!==void 0)return r;let e=document.querySelector(`script[data-camox-data]`);try{r=JSON.parse(e?e.textContent:decodeURIComponent(o)),(!r||typeof r!=`object`||Array.isArray(r))&&(r={})}catch{r={}}return i=JSON.stringify(r),r},c=(()=>{let e={setTimeout:window.setTimeout.bind(window),setInterval:window.setInterval.bind(window),clearTimeout:window.clearTimeout.bind(window)},t=new Map;window.setTimeout=function(n,r,...i){if(typeof n!=`function`)return e.setTimeout(n,r,...i);let a=e.setTimeout(function(){return t.delete(a),n.apply(this,arguments)},r,...i);return t.set(a,{kind:`setTimeout`,delay:Number(r)||0}),a},window.setInterval=function(n,r,...i){let a=e.setInterval(n,r,...i);return typeof n==`function`&&t.set(a,{kind:`setInterval`,delay:Number(r)||0}),a};let n=function(n){t.delete(n),e.clearTimeout(n)};return window.clearTimeout=n,window.clearInterval=n,{later:e.setTimeout,get live(){return[...t.values()]}}})(),l=(()=>{let e=new Set,t=[],n=Object.freeze({tokens:Object.freeze({}),colors:Object.freeze({})}),r=document.createElement(`style`);r.textContent=`body { visibility: hidden !important; }`,document.head.append(r);let i=()=>r.remove();c.later(i,300);let a=e=>Number.isFinite(e)?Math.min(255,Math.max(0,Math.round(e))):0;return{get current(){return n},receive(r){let o={},s={},c=r.colors||{};for(let[e,t]of Object.entries(r.tokens||{})){if(!e.startsWith(`--`)||typeof t!=`string`)continue;o[e]=t;let n=Object.hasOwn(c,e)?c[e]:null;n&&typeof n==`object`&&(s[e]=Object.freeze({r:a(n.r),g:a(n.g),b:a(n.b)}))}let l=document.documentElement.style;for(let e of t)Object.hasOwn(o,e)||l.removeProperty(e);for(let[e,t]of Object.entries(o))l.setProperty(e,t);t=Object.keys(o),i(),n=Object.freeze({tokens:Object.freeze(o),colors:Object.freeze(s)});for(let t of[...e])t(n)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}})(),u=new Set,d=null,f=null,p=null,m=()=>{let e=Math.ceil(document.documentElement.getBoundingClientRect().height);e!==p&&(p=e,h(`height`,{height:p}))},h=(n,r)=>{t&&e.postMessage({...r,camox:n,channel:t},`*`)},g=()=>h(`observe-viewport`,{observe:u.size>0}),_=(e,t,n,r)=>{let i=(e,t,n)=>3*(1-n)*(1-n)*n*e+3*(1-n)*n*n*t+n*n*n,a=(e,t,n)=>3*(1-n)*(1-n)*e+6*(1-n)*n*(t-e)+3*n*n*(1-t);return o=>{if(o<=0)return 0;if(o>=1)return 1;let s=o;for(let c=0;c<8;c++){let c=i(e,n,s)-o;if(Math.abs(c)<1e-7)return i(t,r,s);let l=a(e,n,s);if(Math.abs(l)<1e-6)break;s-=c/l}let c=0,l=1;s=o;for(let t=0;t<40;t++){let t=i(e,n,s);if(Math.abs(t-o)<1e-7)break;t<o?c=s:l=s,s=(c+l)/2}return i(t,r,s)}},v=Object.freeze({travel:[.45,0,.1,1],arrive:[.25,.8,.25,1]}),y=Object.freeze({linear:e=>Math.min(1,Math.max(0,e)),travel:_(...v.travel),arrive:_(...v.arrive)}),b=e=>typeof e==`string`&&Object.hasOwn(y,e),x=e=>{if(e===void 0)return y.linear;if(typeof e==`function`)return e;if(b(e))return y[e];throw TypeError(`Unknown easing `+String(e)+`: use travel, arrive, linear or a function`)},S=(()=>{let e=[`transform`,`opacity`],t=e=>e===`linear`?`linear`:`cubic-bezier(`+v[e].join(`, `)+`)`,n=()=>Object.freeze({width:window.innerWidth,height:window.innerHeight}),r=(e,t)=>({index:e.index,text:e.name+` is skipped, so nothing it animates moves: `+t+`.`}),i=(e,t)=>{if(!e||typeof e!=`object`)return`a Track is an object`;let{el:n,at:r,keyframes:i}=e;if(!n||typeof n.animate!=`function`)return`it needs an el, the element it moves`;if(typeof r!=`number`||!(r>=0))return`it needs an at, when it starts in ms`;let a=e.duration;if(typeof a!=`number`||!(a>=0)||!(r+a<=t))return`it needs a duration in ms, and must end by the Timeline's duration (`+t+` ms)`;if(e.ease!==void 0&&!b(e.ease))return`it has an unknown easing `+String(e.ease)+`: use travel, arrive or linear`;if(typeof i!=`function`&&(!i||typeof i!=`object`))return`it needs keyframes, or a function of the artifact's size giving them`},a=(e,t)=>{if(e===void 0)return{tracks:[],skipped:[]};if(!Array.isArray(e))throw TypeError(`A Timeline's tracks must be an array of Tracks`);let n=[],a=[];return e.forEach((e,o)=>{let s=`Track `+o,c=i(e,t);if(c){a.push(r({index:o,name:s},c));return}let{el:l,at:u,keyframes:d}=e,f=e.ease===void 0?`linear`:e.ease;n.push({name:s,index:o,el:l,at:u,end:u+e.duration,ease:f,keyframes:d})}),{tracks:n,skipped:a}},o=(t,n)=>{let r=typeof t.keyframes==`function`?t.keyframes(n):t.keyframes;if(!r||typeof r!=`object`)return{problem:`its keyframes must be an object`};let i={};for(let[t,n]of Object.entries(r)){if(!e.includes(t))return{problem:`Tracks animate transform and opacity only, not `+t+`: draw it from render(t)`};if(!Array.isArray(n)||n.length!==2)return{problem:`its `+t+` needs two values, [from, to]`};i[t]=n}return{frames:i}},s=(e,i,a)=>{let s=new Map,c=[],l=n();for(let t of[...e].sort((e,t)=>e.at-t.at||e.index-t.index)){let{frames:e,problem:n}=o(t,l);if(n)c.push(r(t,n));else for(let[n,[r,i]]of Object.entries(e)){s.has(t.el)||s.set(t.el,new Map);let e=s.get(t.el);e.has(n)||e.set(n,[]),e.get(n).push({track:t,from:r,to:i})}}let u=[],d=[];for(let[e,n]of s)for(let[r,o]of n){let n=(e,n,a)=>({offset:e/i,[r]:n,easing:t(a)}),s=[n(0,o[0].from,`linear`)],c=0,l=null,f=null;for(let{track:t,from:i,to:a}of o){if(f&&t.at<f.end){let n=Math.min(f.end,t.end);d.push({el:e,property:r,tracks:[f.index,t.index],from:t.at,to:n})}(!f||t.end>f.end)&&(f=t);let o=Math.max(t.at,c),u=Math.max(t.end,o);l&&s.push(n(o,l.to,`linear`)),s.push(n(o,i,t.ease),n(u,a,`linear`)),c=u,l={track:t,to:a}}s.push(n(i,l.to,`linear`)),u.push(e.animate(s,{duration:i,iterations:a?1/0:1,fill:`both`}))}return{animations:u,overlaps:d,skipped:c}};return(e,t,n)=>{let{tracks:r,skipped:i}=a(e,t),o=s(r,t,n),c=null,l=0,u=()=>{for(let e of o.animations)c===null?(e.pause(),e.currentTime=Math.min(t,Math.max(0,l))):e.startTime=c};return r.some(e=>typeof e.keyframes==`function`)&&window.addEventListener(`resize`,()=>{for(let e of o.animations)e.cancel();o=s(r,t,n),u()}),{get animations(){return o.animations},get overlaps(){return o.overlaps},get skipped(){return[...i,...o.skipped].sort((e,t)=>e.index-t.index)},play(e){c=e,u()},seek(e){c=null,l=e,u()}}}})(),C=(()=>{let e=null,t=null,n=!1,r=0,i=null,a=0,o=null,s=!1,l=t=>{let{duration:n,loop:r}=e;return r?(t%n+n)%n:Math.min(n,Math.max(0,t))},d=[],f=0,p=t=>{f=t;for(let e of d)e(t);e.render&&e.render(t)},m=e=>{t.seek(e),p(e)},_=()=>{let e=document.timeline?document.timeline.currentTime:null;return typeof e==`number`?e:performance.now()},v=()=>{r&&cancelAnimationFrame(r),r=0},y=t=>{r=0;let n=l(t-i);p(n),e.loop||n<e.duration?r=requestAnimationFrame(y):(a=e.duration,i=null)},b=()=>{v(),i=_()-a,t.play(i),r=requestAnimationFrame(y)},x=()=>{i!==null&&(a=_()-i,i=null,v(),m(l(a)))},C=()=>{if(n||s)return;let{loop:t,duration:r}=e;(o===null?t:o)&&(t||a<r)?i===null&&b():x()},w=e=>{o=e.visibleRatio>0,C()},T=()=>{let{poster:t,marks:n,loop:r,duration:i}=e;return t===null?r?0:i:n[t]},E=e=>{let t=!1,n=()=>{t||(t=!0,e())};requestAnimationFrame(()=>requestAnimationFrame(n)),c.later(n,200)};return{inspect(n){if(!e)return{animations:[],skipped:[],overlaps:[],ends:null};let{animations:r,skipped:i,overlaps:a}=t;if(!e.loop||!n)return{animations:r,skipped:i,overlaps:a,ends:null};let o=f,s=e.duration-1;try{m(0);let e=n();return m(s),{animations:r,skipped:i,overlaps:a,ends:{first:e,last:n(),at:s}}}finally{m(o)}},register(t,n){if(!e)throw Error(`camox.`+t+` needs a Timeline: call camox.timeline first`);d.push(n),n(f)},declare(r){if(e)throw Error(`camox.timeline can be called once per artifact`);if(!r||typeof r!=`object`)throw TypeError(`camox.timeline needs its options: duration, marks, render`);let{duration:i}=r;if(typeof i!=`number`||!Number.isFinite(i)||i<=0)throw TypeError(`camox.timeline needs a duration: a positive number of milliseconds`);let a={};for(let[e,t]of Object.entries(r.marks||{})){if(typeof t!=`number`||!(t>=0&&t<=i))throw RangeError(`The Mark `+e+` must be a time from 0 to the duration`);a[e]=t}let o=r.poster===void 0?null:String(r.poster);if(o!==null&&!Object.hasOwn(a,o))throw RangeError(`The poster `+o+` must name a Mark`);let{render:c}=r;if(c!==void 0&&typeof c!=`function`)throw TypeError(`A Timeline's render must be a function of the time`);let l=r.loop===!0;t=S(r.tracks,i,l),e=Object.freeze({duration:i,loop:l,marks:Object.freeze(a),poster:o,render:c}),s=!n&&typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches,m(s?T():0),!(n||s)&&(u.add(w),u.size===1&&g(),C())},capture(){n=!0,e&&(v(),i=null,u.delete(w)&&u.size===0&&g(),m(0))},describe(t){if(!n)return;let{duration:r,loop:i,marks:a,poster:o}=e||{};h(`timeline`,{id:t.id,timeline:e?{duration:r,loop:i,marks:a,poster:o}:null})},seek(t){if(!n)return;let r=Number(t.t),i;if(e&&Number.isFinite(r))try{m(l(r))}catch(e){i=`render(`+r+`) threw: `+String(e&&e.message?e.message:e)}E(()=>h(`seeked`,i===void 0?{id:t.id}:{id:t.id,error:i}))}}})(),w=(()=>{let e=(e,t)=>{if(!(t instanceof Element))throw TypeError(`camox.`+e+` needs an element to write in`);return t},t=(e,t,n,r,i)=>{if(n===void 0&&r!==void 0)return r;if(typeof n!=`number`||!Number.isFinite(n)||n<0||i&&n===0)throw TypeError(`camox.`+e+`'s `+t+` must be a `+(i?`positive `:``)+`number`);return n},n=e=>{let[,t=``]=String(e).split(`.`);return/^[0-9]+$/.test(t)?Math.min(20,t.length):0};return{type(n,r,i={}){let a=e(`type`,n),o=t(`type`,`at`,i.at,0),s=t(`type`,`cps`,i.cps,20,!0),c=typeof Intl.Segmenter==`function`?Array.from(new Intl.Segmenter().segment(String(r)),e=>e.segment):Array.from(String(r)),l=o+c.length*1e3/s,u=document.createElement(`span`),d=document.createTextNode(``),f=document.createElement(`span`);f.setAttribute(`aria-hidden`,`true`),f.setAttribute(`data-camox-caret`,``),f.style.cssText=`border-left: max(1px, 0.07em) solid currentColor; margin-right: calc(-1 * max(1px, 0.07em));`,u.append(d,f),a.replaceChildren(u);let p=!0;C.register(`type`,e=>{let t=e<o?0:Math.min(c.length,Math.floor((e-o)*s/1e3)+1),n=c.slice(0,t).join(``);d.data!==n&&(d.data=n);let r=e<o?o-e:e-l,i=r<0||Math.floor(r/530)%2==0;i!==p&&(p=i,f.style.visibility=i?``:`hidden`)})},count(r,i){let a=e(`count`,r);if(!i||typeof i.to!=`number`||!Number.isFinite(i.to))throw TypeError(`camox.count needs the number to count to`);let{to:o}=i,s=i.from===void 0?0:i.from;if(typeof s!=`number`||!Number.isFinite(s))throw TypeError(`camox.count's from must be a number`);let c=t(`count`,`at`,i.at,0),l=t(`count`,`duration`,i.duration,1e3,!0),u=x(i.ease===void 0?`arrive`:i.ease),d=i.format;if(d===void 0){let e=Math.max(n(s),n(o)),t=new Intl.NumberFormat(void 0,{minimumFractionDigits:e,maximumFractionDigits:e});d=e=>t.format(e)}else if(typeof d!=`function`)throw TypeError(`camox.count's format must be a function of the value`);let f=null;C.register(`count`,e=>{let t=e>=c+l?o:s+(o-s)*u(Math.max(0,e-c)/l),n=String(d(t));n!==f&&(f=n,a.textContent=n)})}}})(),T=(()=>{let e=!1,t=new Set,n=/-?(?:[0-9]+[.]?[0-9]*|[.][0-9]+)(?:e[-+]?[0-9]+)?/g,r=e=>{if(!e||!e.tagName)return`an element`;let t=e.tagName.toLowerCase();if(e.id)return t+`#`+e.id;let n=Array.from(e.classList).slice(0,2);if(n.length)return t+`.`+n.join(`.`);let i=e.parentElement;return!i||i===document.documentElement?t:r(i)+` > `+t+`:nth-child(`+(Array.from(i.children).indexOf(e)+1)+`)`},i=e=>{let t=e.effect,n=t&&t.target,i=(n?r(n):`no element`)+(t&&t.pseudoElement||``);return typeof CSSAnimation==`function`&&e instanceof CSSAnimation?`The CSS animation `+e.animationName+` on `+i+` plays on its own, outside the Timeline, so captures can't seek it: make it a Track, or draw it from render(t).`:typeof CSSTransition==`function`&&e instanceof CSSTransition?`A CSS transition of `+e.transitionProperty+` on `+i+` ran without a visitor's hover or press: motion that plays on its own goes through the Timeline, as a Track or from render(t).`:`A Web Animation on `+i+` was made by the source and plays outside the Timeline: declare it as a Track, or pause it and set its currentTime from render(t).`},a=()=>{if(!e||typeof document.getAnimations!=`function`)return;let n=new Set(C.inspect().animations);for(let e of document.getAnimations())n.has(e)||e.playState===`paused`||e.playState===`idle`||t.add(i(e))},o=()=>{let e=new Map;for(let{kind:t,delay:n}of c.live){let r=t+` `+n;e.has(r)||e.set(r,{kind:t,delay:n,count:0}),e.get(r).count++}return[...e.values()].map(({kind:e,delay:t,count:n})=>({kind:`timer`,text:(e===`setInterval`?`A setInterval every `+t+` ms is still running`:`A setTimeout of `+t+` ms is still pending`)+(n>1?` (`+n+` of them)`:``)+`: time stands still in captures, so what timers move never shows there, and they don't follow the Timeline's loop. Draw it from render(t) instead.`}))},s=e=>{let t=new Map,n=(r,i,a)=>{let o=getComputedStyle(r),s=a&&o.display!==`none`,c=i*Number(o.opacity),l=s&&o.visibility!==`hidden`&&c>.01;if(!r.hasAttribute(`data-camox-caret`)){let n={};for(let e of r.attributes)n[e.name+` attribute`]=e.value;let i=``;for(let e of r.childNodes)e.nodeType===Node.TEXT_NODE&&(i+=e.data);if(i&&(n.text=i),e.has(r)&&(n.transform=new DOMMatrix(o.transform).toString(),n.opacity=o.opacity),r instanceof HTMLCanvasElement)try{n[`canvas drawing`]=r.toDataURL()}catch{}t.set(r,{visible:l,values:n})}for(let e of r.children)n(e,c,s)};return n(document.documentElement,1,!0),t},l=(e,t)=>{if(e===t)return!0;if(typeof e!=`string`||typeof t!=`string`||e.replace(n,`0`)!==t.replace(n,`0`))return!1;let r=t.match(n)||[];return(e.match(n)||[]).every((e,t)=>{let n=Number(e),i=Number(r[t]);return Math.abs(n-i)<=.01*Math.max(1,Math.abs(n),Math.abs(i))})},u=e=>JSON.stringify(e.length>40?e.slice(0,39)+`…`:e),d=()=>{let e=new Set(C.inspect().animations.map(e=>e.effect&&e.effect.target)),t;try{t=C.inspect(()=>s(e)).ends}catch{return null}if(!t)return null;let{first:n,last:i,at:a}=t,o=[];for(let e of new Set([...n.keys(),...i.keys()])){let t=n.get(e),s=i.get(e);if(!t||!s)(t||s).visible&&o.push(r(e)+` is there at only one end`);else if(t.visible||s.visible)for(let n of new Set([...Object.keys(t.values),...Object.keys(s.values)])){let i=t.values[n],c=s.values[n];l(i,c)||o.push(n===`canvas drawing`?r(e)+`'s canvas drawing`:r(e)+`'s `+n+` (`+(i===void 0?`none`:u(i))+` at 0 ms, `+(c===void 0?`none`:u(c))+` at `+a+` ms)`)}}if(!o.length)return null;let c=o.slice(0,4).join(`; `)+(o.length>4?`; and `+(o.length-4)+` more`:``);return{kind:`loop-cut`,text:`Loop cut: the last frame (`+a+` ms) differs from the first (0 ms), so the loop jumps at its restart: `+c+`. Bring them back to how they start before the end, or hide them while they reset.`}};return{get watching(){return e},watch(){e=!0,a()},sample:a,report(){let e=d();a();let n=[...t].map(e=>({kind:`animation`,text:e}));n.push(...o());let{skipped:i,overlaps:s}=C.inspect();for(let{text:e}of i)n.push({kind:`track`,text:e});for(let{el:e,property:t,tracks:i,from:a,to:o}of s)n.push({kind:`overlap`,text:`Tracks `+i[0]+` and `+i[1]+` both animate the `+t+` of `+r(e)+`, from `+a+` to `+o+` ms: the later one waits for the earlier to end, so it jumps. Start it once the earlier one ends.`});return e&&n.push(e),n}}})(),E={studio(e){let t=e.studio;n=t?Object.freeze({editing:!!t.editing,canvas:!!t.canvas}):null},data(e){s();let t=JSON.stringify(e.data??{});if(t!==i){r=JSON.parse(t),i=t;for(let e of[...a])e(r)}},theme:l.receive,measure(){p=null,f||(f=new ResizeObserver(m),f.observe(document.documentElement)),m()},viewport(e){let{progress:t,visibleRatio:n}=e;if(typeof t==`number`&&typeof n==`number`){d=Object.freeze({progress:t,visibleRatio:n}),document.documentElement.style.setProperty(`--camox-progress`,String(t));for(let e of u)e(d)}},capture(e){C.capture(e),T.watch()},timeline:C.describe,seek(e){C.seek(e),T.sample()},guardrail(e){T.watching&&h(`guardrail`,{id:e.id,findings:T.report()})}};window.addEventListener(`message`,e=>{let n=e.data;if(n&&typeof n.camox==`string`){if(n.camox===`connect`){if(typeof n.channel!=`string`||n.channel===t)return;t=n.channel,h(`ready`),u.size>0&&g()}else t&&n.channel===t&&Object.hasOwn(E,n.camox)&&E[n.camox](n)}});let D=Object.freeze({get studio(){return n},get data(){return s()},onData(e){return a.add(e),()=>{a.delete(e)}},get theme(){return l.current},onTheme:l.subscribe,navigate(e){h(`navigate`,{href:String(e)})},onViewport(e){if(typeof e!=`function`)throw TypeError(`camox.onViewport needs a callback`);let t=t=>e(t);return u.add(t),u.size===1?g():d&&queueMicrotask(()=>u.has(t)&&e(d)),()=>{u.delete(t)&&u.size===0&&g()}},timeline:C.declare,interpolate(e,t,n,r){let[i,a]=t,[o,s]=n,c=x(r&&r.ease),l=a===i?e<i?0:1:(e-i)/(a-i);return o+(s-o)*c(Math.min(1,Math.max(0,l)))},random(e){let t=String(e),n=2166136261;for(let e=0;e<t.length;e++)n=Math.imul(n^t.charCodeAt(e),16777619);return n=Math.imul(n^n>>>16,2246822507),n=Math.imul(n^n>>>13,3266489909),((n^n>>>16)>>>0)/4294967296},type:w.type,count:w.count});Object.defineProperty(window,\"camox\",{value:D,enumerable:!0}),e!==window&&e.postMessage({camox:`hello`},`*`)})();";
56
+ const ARTIFACT_RUNTIME = "(()=>{\"use strict\";let e=window.parent,t=null,n=null,r,i,a=new Set,o=location.hash.slice(1),s=()=>{if(i!==void 0)return r;let e=document.querySelector(`script[data-camox-data]`);try{r=JSON.parse(e?e.textContent:decodeURIComponent(o)),(!r||typeof r!=`object`||Array.isArray(r))&&(r={})}catch{r={}}return i=JSON.stringify(r),r},c=(()=>{let e={setTimeout:window.setTimeout.bind(window),setInterval:window.setInterval.bind(window),clearTimeout:window.clearTimeout.bind(window)},t=new Map;window.setTimeout=function(n,r,...i){if(typeof n!=`function`)return e.setTimeout(n,r,...i);let a=e.setTimeout(function(){return t.delete(a),n.apply(this,arguments)},r,...i);return t.set(a,{kind:`setTimeout`,delay:Number(r)||0}),a},window.setInterval=function(n,r,...i){let a=e.setInterval(n,r,...i);return typeof n==`function`&&t.set(a,{kind:`setInterval`,delay:Number(r)||0}),a};let n=function(n){t.delete(n),e.clearTimeout(n)};return window.clearTimeout=n,window.clearInterval=n,{later:e.setTimeout,get live(){return[...t.values()]}}})(),l=(()=>{let e=new Set,t=[],n=Object.freeze({tokens:Object.freeze({}),colors:Object.freeze({})}),r=document.createElement(`style`);r.textContent=`body { visibility: hidden !important; }`,document.head.append(r);let i=()=>r.remove();c.later(i,300);let a=e=>Number.isFinite(e)?Math.min(255,Math.max(0,Math.round(e))):0;return{get current(){return n},receive(r){let o={},s={},c=r.colors||{};for(let[e,t]of Object.entries(r.tokens||{})){if(!e.startsWith(`--`)||typeof t!=`string`)continue;o[e]=t;let n=Object.hasOwn(c,e)?c[e]:null;n&&typeof n==`object`&&(s[e]=Object.freeze({r:a(n.r),g:a(n.g),b:a(n.b)}))}let l=document.documentElement.style;for(let e of t)Object.hasOwn(o,e)||l.removeProperty(e);for(let[e,t]of Object.entries(o))l.setProperty(e,t);t=Object.keys(o),i(),n=Object.freeze({tokens:Object.freeze(o),colors:Object.freeze(s)});for(let t of[...e])t(n)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}})(),u=new Set,d=null,f=null,p=null,m=()=>{let e=Math.ceil(document.documentElement.getBoundingClientRect().height);e!==p&&(p=e,h(`height`,{height:p}))},h=(n,r)=>{t&&e.postMessage({...r,camox:n,channel:t},`*`)},g=()=>h(`observe-viewport`,{observe:u.size>0}),_=(e,t,n,r)=>{let i=(e,t,n)=>3*(1-n)*(1-n)*n*e+3*(1-n)*n*n*t+n*n*n,a=(e,t,n)=>3*(1-n)*(1-n)*e+6*(1-n)*n*(t-e)+3*n*n*(1-t);return o=>{if(o<=0)return 0;if(o>=1)return 1;let s=o;for(let c=0;c<8;c++){let c=i(e,n,s)-o;if(Math.abs(c)<1e-7)return i(t,r,s);let l=a(e,n,s);if(Math.abs(l)<1e-6)break;s-=c/l}let c=0,l=1;s=o;for(let t=0;t<40;t++){let t=i(e,n,s);if(Math.abs(t-o)<1e-7)break;t<o?c=s:l=s,s=(c+l)/2}return i(t,r,s)}},v=Object.freeze({travel:[.45,0,.1,1],arrive:[.25,.8,.25,1]}),y=Object.freeze({linear:e=>Math.min(1,Math.max(0,e)),travel:_(...v.travel),arrive:_(...v.arrive)}),b=e=>typeof e==`string`&&Object.hasOwn(y,e),x=e=>{if(e===void 0)return y.linear;if(typeof e==`function`)return e;if(b(e))return y[e];throw TypeError(`Unknown easing `+String(e)+`: use travel, arrive, linear or a function`)},S=(()=>{let e=[`transform`,`opacity`],t=e=>e===`linear`?`linear`:`cubic-bezier(`+v[e].join(`, `)+`)`,n=()=>Object.freeze({width:window.innerWidth,height:window.innerHeight}),r=(e,t)=>({index:e.index,text:e.name+` is skipped, so nothing it animates moves: `+t+`.`}),i=(e,t)=>{if(!e||typeof e!=`object`)return`a Track is an object`;let{el:n,at:r,keyframes:i}=e;if(!n||typeof n.animate!=`function`)return`it needs an el, the element it moves`;if(typeof r!=`number`||!(r>=0))return`it needs an at, when it starts in ms`;let a=e.duration;if(typeof a!=`number`||!(a>=0)||!(r+a<=t))return`it needs a duration in ms, and must end by the Timeline's duration (`+t+` ms)`;if(e.ease!==void 0&&!b(e.ease))return`it has an unknown easing `+String(e.ease)+`: use travel, arrive or linear`;if(typeof i!=`function`&&(!i||typeof i!=`object`))return`it needs keyframes, or a function of the artifact's size giving them`},a=(e,t)=>{if(e===void 0)return{tracks:[],skipped:[]};if(!Array.isArray(e))throw TypeError(`A Timeline's tracks must be an array of Tracks`);let n=[],a=[];return e.forEach((e,o)=>{let s=`Track `+o,c=i(e,t);if(c){a.push(r({index:o,name:s},c));return}let{el:l,at:u,keyframes:d}=e,f=e.ease===void 0?`linear`:e.ease;n.push({name:s,index:o,el:l,at:u,end:u+e.duration,ease:f,keyframes:d})}),{tracks:n,skipped:a}},o=(t,n)=>{let r=typeof t.keyframes==`function`?t.keyframes(n):t.keyframes;if(!r||typeof r!=`object`)return{problem:`its keyframes must be an object`};let i={};for(let[t,n]of Object.entries(r)){if(!e.includes(t))return{problem:`Tracks animate transform and opacity only, not `+t+`: draw it from render(t)`};if(!Array.isArray(n)||n.length!==2)return{problem:`its `+t+` needs two values, [from, to]`};i[t]=n}return{frames:i}},s=(e,i,a)=>{let s=new Map,c=[],l=n();for(let t of[...e].sort((e,t)=>e.at-t.at||e.index-t.index)){let{frames:e,problem:n}=o(t,l);if(n)c.push(r(t,n));else for(let[n,[r,i]]of Object.entries(e)){s.has(t.el)||s.set(t.el,new Map);let e=s.get(t.el);e.has(n)||e.set(n,[]),e.get(n).push({track:t,from:r,to:i})}}let u=[],d=[];for(let[e,n]of s)for(let[r,o]of n){let n=(e,n,a)=>({offset:e/i,[r]:n,easing:t(a)}),s=[n(0,o[0].from,`linear`)],c=0,l=null,f=null;for(let{track:t,from:i,to:a}of o){if(f&&t.at<f.end){let n=Math.min(f.end,t.end);d.push({el:e,property:r,tracks:[f.index,t.index],from:t.at,to:n})}(!f||t.end>f.end)&&(f=t);let o=Math.max(t.at,c),u=Math.max(t.end,o);l&&s.push(n(o,l.to,`linear`)),s.push(n(o,i,t.ease),n(u,a,`linear`)),c=u,l={track:t,to:a}}s.push(n(i,l.to,`linear`)),u.push(e.animate(s,{duration:i,iterations:a?1/0:1,fill:`both`}))}return{animations:u,overlaps:d,skipped:c}};return(e,t,n)=>{let{tracks:r,skipped:i}=a(e,t),o=s(r,t,n),c=null,l=0,u=()=>{for(let e of o.animations)c===null?(e.pause(),e.currentTime=Math.min(t,Math.max(0,l))):e.startTime=c};return r.some(e=>typeof e.keyframes==`function`)&&window.addEventListener(`resize`,()=>{for(let e of o.animations)e.cancel();o=s(r,t,n),u()}),{get animations(){return o.animations},get overlaps(){return o.overlaps},get skipped(){return[...i,...o.skipped].sort((e,t)=>e.index-t.index)},play(e){c=e,u()},seek(e){c=null,l=e,u()}}}})(),C=(()=>{let e=null,t=null,n=!1,r=0,i=null,a=0,o=null,s=!1,l=t=>{let{duration:n,loop:r}=e;return r?(t%n+n)%n:Math.min(n,Math.max(0,t))},d=[],f=0,p=t=>{f=t;for(let e of d)e(t);e.render&&e.render(t)},m=e=>{t.seek(e),p(e)},_=()=>{let e=document.timeline?document.timeline.currentTime:null;return typeof e==`number`?e:performance.now()},v=()=>{r&&cancelAnimationFrame(r),r=0},y=t=>{r=0;let n=l(t-i);p(n),e.loop||n<e.duration?r=requestAnimationFrame(y):(a=e.duration,i=null)},b=()=>{v(),i=_()-a,t.play(i),r=requestAnimationFrame(y)},x=()=>{i!==null&&(a=_()-i,i=null,v(),m(l(a)))},C=()=>{if(n||s)return;let{loop:t,duration:r}=e;(o===null?t:o)&&(t||a<r)?i===null&&b():x()},w=e=>{o=e.visibleRatio>0,C()},T=()=>{let{poster:t,marks:n,loop:r,duration:i}=e;return t===null?r?0:i:n[t]},E=e=>{let t=!1,n=()=>{t||(t=!0,e())};requestAnimationFrame(()=>requestAnimationFrame(n)),c.later(n,200)};return{inspect(n){if(!e)return{animations:[],skipped:[],overlaps:[],ends:null};let{animations:r,skipped:i,overlaps:a}=t;if(!e.loop||!n)return{animations:r,skipped:i,overlaps:a,ends:null};let o=f,s=e.duration-1;try{m(0);let e=n();return m(s),{animations:r,skipped:i,overlaps:a,ends:{first:e,last:n(),at:s}}}finally{m(o)}},register(t,n){if(!e)throw Error(`camox.`+t+` needs a Timeline: call camox.timeline first`);d.push(n),n(f)},declare(r){if(e)throw Error(`camox.timeline can be called once per artifact`);if(!r||typeof r!=`object`)throw TypeError(`camox.timeline needs its options: duration, marks, render`);let{duration:i}=r;if(typeof i!=`number`||!Number.isFinite(i)||i<=0)throw TypeError(`camox.timeline needs a duration: a positive number of milliseconds`);let a={};for(let[e,t]of Object.entries(r.marks||{})){if(typeof t!=`number`||!(t>=0&&t<=i))throw RangeError(`The Mark `+e+` must be a time from 0 to the duration`);a[e]=t}let o=r.poster===void 0?null:String(r.poster);if(o!==null&&!Object.hasOwn(a,o))throw RangeError(`The poster `+o+` must name a Mark`);let{render:c}=r;if(c!==void 0&&typeof c!=`function`)throw TypeError(`A Timeline's render must be a function of the time`);let l=r.loop===!0;t=S(r.tracks,i,l),e=Object.freeze({duration:i,loop:l,marks:Object.freeze(a),poster:o,render:c}),s=!n&&typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches,m(n||s?T():0),!(n||s)&&(u.add(w),u.size===1&&g(),C())},capture(){n=!0,e&&(v(),i=null,u.delete(w)&&u.size===0&&g(),m(T()))},describe(t){if(!n)return;let{duration:r,loop:i,marks:a,poster:o}=e||{};h(`timeline`,{id:t.id,timeline:e?{duration:r,loop:i,marks:a,poster:o}:null})},seek(t){if(!n)return;let r=Number(t.t),i;if(e&&Number.isFinite(r))try{m(l(r))}catch(e){i=`render(`+r+`) threw: `+String(e&&e.message?e.message:e)}E(()=>h(`seeked`,i===void 0?{id:t.id}:{id:t.id,error:i}))}}})(),w=(()=>{let e=(e,t)=>{if(!(t instanceof Element))throw TypeError(`camox.`+e+` needs an element to write in`);return t},t=(e,t,n,r,i)=>{if(n===void 0&&r!==void 0)return r;if(typeof n!=`number`||!Number.isFinite(n)||n<0||i&&n===0)throw TypeError(`camox.`+e+`'s `+t+` must be a `+(i?`positive `:``)+`number`);return n},n=e=>{let[,t=``]=String(e).split(`.`);return/^[0-9]+$/.test(t)?Math.min(20,t.length):0};return{type(n,r,i={}){let a=e(`type`,n),o=t(`type`,`at`,i.at,0),s=t(`type`,`cps`,i.cps,20,!0),c=typeof Intl.Segmenter==`function`?Array.from(new Intl.Segmenter().segment(String(r)),e=>e.segment):Array.from(String(r)),l=o+c.length*1e3/s,u=document.createElement(`span`),d=document.createTextNode(``),f=document.createElement(`span`);f.setAttribute(`aria-hidden`,`true`),f.setAttribute(`data-camox-caret`,``),f.style.cssText=`border-left: max(1px, 0.07em) solid currentColor; margin-right: calc(-1 * max(1px, 0.07em));`,u.append(d,f),a.replaceChildren(u);let p=!0;C.register(`type`,e=>{let t=e<o?0:Math.min(c.length,Math.floor((e-o)*s/1e3)+1),n=c.slice(0,t).join(``);d.data!==n&&(d.data=n);let r=e<o?o-e:e-l,i=r<0||Math.floor(r/530)%2==0;i!==p&&(p=i,f.style.visibility=i?``:`hidden`)})},count(r,i){let a=e(`count`,r);if(!i||typeof i.to!=`number`||!Number.isFinite(i.to))throw TypeError(`camox.count needs the number to count to`);let{to:o}=i,s=i.from===void 0?0:i.from;if(typeof s!=`number`||!Number.isFinite(s))throw TypeError(`camox.count's from must be a number`);let c=t(`count`,`at`,i.at,0),l=t(`count`,`duration`,i.duration,1e3,!0),u=x(i.ease===void 0?`arrive`:i.ease),d=i.format;if(d===void 0){let e=Math.max(n(s),n(o)),t=new Intl.NumberFormat(void 0,{minimumFractionDigits:e,maximumFractionDigits:e});d=e=>t.format(e)}else if(typeof d!=`function`)throw TypeError(`camox.count's format must be a function of the value`);let f=null;C.register(`count`,e=>{let t=e>=c+l?o:s+(o-s)*u(Math.max(0,e-c)/l),n=String(d(t));n!==f&&(f=n,a.textContent=n)})}}})(),T=(()=>{let e=!1,t=new Set,n=/-?(?:[0-9]+[.]?[0-9]*|[.][0-9]+)(?:e[-+]?[0-9]+)?/g,r=e=>{if(!e||!e.tagName)return`an element`;let t=e.tagName.toLowerCase();if(e.id)return t+`#`+e.id;let n=Array.from(e.classList).slice(0,2);if(n.length)return t+`.`+n.join(`.`);let i=e.parentElement;return!i||i===document.documentElement?t:r(i)+` > `+t+`:nth-child(`+(Array.from(i.children).indexOf(e)+1)+`)`},i=e=>{let t=e.effect,n=t&&t.target,i=(n?r(n):`no element`)+(t&&t.pseudoElement||``);return typeof CSSAnimation==`function`&&e instanceof CSSAnimation?`The CSS animation `+e.animationName+` on `+i+` plays on its own, outside the Timeline, so captures can't seek it: make it a Track, or draw it from render(t).`:typeof CSSTransition==`function`&&e instanceof CSSTransition?`A CSS transition of `+e.transitionProperty+` on `+i+` ran without a visitor's hover or press: motion that plays on its own goes through the Timeline, as a Track or from render(t).`:`A Web Animation on `+i+` was made by the source and plays outside the Timeline: declare it as a Track, or pause it and set its currentTime from render(t).`},a=()=>{if(!e||typeof document.getAnimations!=`function`)return;let n=new Set(C.inspect().animations);for(let e of document.getAnimations())n.has(e)||e.playState===`paused`||e.playState===`idle`||t.add(i(e))},o=()=>{let e=new Map;for(let{kind:t,delay:n}of c.live){let r=t+` `+n;e.has(r)||e.set(r,{kind:t,delay:n,count:0}),e.get(r).count++}return[...e.values()].map(({kind:e,delay:t,count:n})=>({kind:`timer`,text:(e===`setInterval`?`A setInterval every `+t+` ms is still running`:`A setTimeout of `+t+` ms is still pending`)+(n>1?` (`+n+` of them)`:``)+`: time stands still in captures, so what timers move never shows there, and they don't follow the Timeline's loop. Draw it from render(t) instead.`}))},s=e=>{let t=new Map,n=(r,i,a)=>{let o=getComputedStyle(r),s=a&&o.display!==`none`,c=i*Number(o.opacity),l=s&&o.visibility!==`hidden`&&c>.01;if(!r.hasAttribute(`data-camox-caret`)){let n={};for(let e of r.attributes)n[e.name+` attribute`]=e.value;let i=``;for(let e of r.childNodes)e.nodeType===Node.TEXT_NODE&&(i+=e.data);if(i&&(n.text=i),e.has(r)&&(n.transform=new DOMMatrix(o.transform).toString(),n.opacity=o.opacity),r instanceof HTMLCanvasElement)try{n[`canvas drawing`]=r.toDataURL()}catch{}t.set(r,{visible:l,values:n})}for(let e of r.children)n(e,c,s)};return n(document.documentElement,1,!0),t},l=(e,t)=>{if(e===t)return!0;if(typeof e!=`string`||typeof t!=`string`||e.replace(n,`0`)!==t.replace(n,`0`))return!1;let r=t.match(n)||[];return(e.match(n)||[]).every((e,t)=>{let n=Number(e),i=Number(r[t]);return Math.abs(n-i)<=.01*Math.max(1,Math.abs(n),Math.abs(i))})},u=e=>JSON.stringify(e.length>40?e.slice(0,39)+`…`:e),d=()=>{let e=new Set(C.inspect().animations.map(e=>e.effect&&e.effect.target)),t;try{t=C.inspect(()=>s(e)).ends}catch{return null}if(!t)return null;let{first:n,last:i,at:a}=t,o=[];for(let e of new Set([...n.keys(),...i.keys()])){let t=n.get(e),s=i.get(e);if(!t||!s)(t||s).visible&&o.push(r(e)+` is there at only one end`);else if(t.visible||s.visible)for(let n of new Set([...Object.keys(t.values),...Object.keys(s.values)])){let i=t.values[n],c=s.values[n];l(i,c)||o.push(n===`canvas drawing`?r(e)+`'s canvas drawing`:r(e)+`'s `+n+` (`+(i===void 0?`none`:u(i))+` at 0 ms, `+(c===void 0?`none`:u(c))+` at `+a+` ms)`)}}if(!o.length)return null;let c=o.slice(0,4).join(`; `)+(o.length>4?`; and `+(o.length-4)+` more`:``);return{kind:`loop-cut`,text:`Loop cut: the last frame (`+a+` ms) differs from the first (0 ms), so the loop jumps at its restart: `+c+`. Bring them back to how they start before the end, or hide them while they reset.`}};return{get watching(){return e},watch(){e=!0,a()},sample:a,report(){let e=d();a();let n=[...t].map(e=>({kind:`animation`,text:e}));n.push(...o());let{skipped:i,overlaps:s}=C.inspect();for(let{text:e}of i)n.push({kind:`track`,text:e});for(let{el:e,property:t,tracks:i,from:a,to:o}of s)n.push({kind:`overlap`,text:`Tracks `+i[0]+` and `+i[1]+` both animate the `+t+` of `+r(e)+`, from `+a+` to `+o+` ms: the later one waits for the earlier to end, so it jumps. Start it once the earlier one ends.`});return e&&n.push(e),n}}})(),E={studio(e){let t=e.studio;n=t?Object.freeze({editing:!!t.editing,canvas:!!t.canvas}):null},data(e){s();let t=JSON.stringify(e.data??{});if(t!==i){r=JSON.parse(t),i=t;for(let e of[...a])e(r)}},theme:l.receive,measure(){p=null,f||(f=new ResizeObserver(m),f.observe(document.documentElement)),m()},viewport(e){let{progress:t,visibleRatio:n}=e;if(typeof t==`number`&&typeof n==`number`){d=Object.freeze({progress:t,visibleRatio:n}),document.documentElement.style.setProperty(`--camox-progress`,String(t));for(let e of u)e(d)}},capture(e){C.capture(e),T.watch()},timeline:C.describe,seek(e){C.seek(e),T.sample()},guardrail(e){T.watching&&h(`guardrail`,{id:e.id,findings:T.report()})}};window.addEventListener(`message`,e=>{let n=e.data;if(n&&typeof n.camox==`string`){if(n.camox===`connect`){if(typeof n.channel!=`string`||n.channel===t)return;t=n.channel,h(`ready`),u.size>0&&g()}else t&&n.channel===t&&Object.hasOwn(E,n.camox)&&E[n.camox](n)}});let D=Object.freeze({get studio(){return n},get data(){return s()},onData(e){return a.add(e),()=>{a.delete(e)}},get theme(){return l.current},onTheme:l.subscribe,navigate(e){h(`navigate`,{href:String(e)})},onViewport(e){if(typeof e!=`function`)throw TypeError(`camox.onViewport needs a callback`);let t=t=>e(t);return u.add(t),u.size===1?g():d&&queueMicrotask(()=>u.has(t)&&e(d)),()=>{u.delete(t)&&u.size===0&&g()}},timeline:C.declare,interpolate(e,t,n,r){let[i,a]=t,[o,s]=n,c=x(r&&r.ease),l=a===i?e<i?0:1:(e-i)/(a-i);return o+(s-o)*c(Math.min(1,Math.max(0,l)))},random(e){let t=String(e),n=2166136261;for(let e=0;e<t.length;e++)n=Math.imul(n^t.charCodeAt(e),16777619);return n=Math.imul(n^n>>>16,2246822507),n=Math.imul(n^n>>>13,3266489909),((n^n>>>16)>>>0)/4294967296},type:w.type,count:w.count});Object.defineProperty(window,\"camox\",{value:D,enumerable:!0}),e!==window&&e.postMessage({camox:`hello`},`*`)})();";
55
57
 
56
58
  //#endregion
57
59
  export { ARTIFACT_RUNTIME };
@@ -16,6 +16,7 @@ import { useRendererAnswer } from "../../renderer/RendererFrames.js";
16
16
  import { PageLocationFieldset } from "./PageLocationFieldset.js";
17
17
  import { PageNicknameField } from "./PageNicknameField.js";
18
18
  import { pageImageUrl } from "../../og/pageImage.js";
19
+ import { ContentFindings, useContentFindings } from "../../studio/components/ContentFindings.js";
19
20
  import { ShikiMarkdown } from "./ShikiMarkdown.js";
20
21
  import { c } from "react/compiler-runtime";
21
22
  import * as React from "react";
@@ -1094,7 +1095,7 @@ const PageSeoContent = (t0) => {
1094
1095
  return t1;
1095
1096
  };
1096
1097
  const PageSeoEditor = (t0) => {
1097
- const $ = c(41);
1098
+ const $ = c(46);
1098
1099
  const { page, metaTitle } = t0;
1099
1100
  let t1;
1100
1101
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
@@ -1104,149 +1105,171 @@ const PageSeoEditor = (t0) => {
1104
1105
  const setSeo = useMutation(t1);
1105
1106
  const localized = useLocalizedPath();
1106
1107
  let t2;
1107
- if ($[1] !== page.id || $[2] !== setSeo) {
1108
- t2 = (checked) => {
1108
+ if ($[1] !== page.id) {
1109
+ t2 = {
1110
+ entity: "page",
1111
+ id: page.id
1112
+ };
1113
+ $[1] = page.id;
1114
+ $[2] = t2;
1115
+ } else t2 = $[2];
1116
+ const findings = useContentFindings(t2);
1117
+ let t3;
1118
+ if ($[3] !== page.id || $[4] !== setSeo) {
1119
+ t3 = (checked) => {
1109
1120
  setSeo.mutate({
1110
1121
  id: page.id,
1111
1122
  aiSeo: checked
1112
1123
  });
1113
1124
  };
1114
- $[1] = page.id;
1115
- $[2] = setSeo;
1116
- $[3] = t2;
1117
- } else t2 = $[3];
1118
- let t3;
1119
- if ($[4] !== page.aiSeo || $[5] !== t2) {
1120
- t3 = /* @__PURE__ */ jsx(Switch, {
1125
+ $[3] = page.id;
1126
+ $[4] = setSeo;
1127
+ $[5] = t3;
1128
+ } else t3 = $[5];
1129
+ let t4;
1130
+ if ($[6] !== page.aiSeo || $[7] !== t3) {
1131
+ t4 = /* @__PURE__ */ jsx(Switch, {
1121
1132
  id: "ai-seo",
1122
1133
  checked: page.aiSeo,
1123
- onCheckedChange: t2
1134
+ onCheckedChange: t3
1124
1135
  });
1125
- $[4] = page.aiSeo;
1126
- $[5] = t2;
1127
- $[6] = t3;
1128
- } else t3 = $[6];
1129
- let t4;
1130
- if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
1131
- t4 = /* @__PURE__ */ jsx(Label, {
1136
+ $[6] = page.aiSeo;
1137
+ $[7] = t3;
1138
+ $[8] = t4;
1139
+ } else t4 = $[8];
1140
+ let t5;
1141
+ if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
1142
+ t5 = /* @__PURE__ */ jsx(Label, {
1132
1143
  htmlFor: "ai-seo",
1133
1144
  children: "AI metadata"
1134
1145
  });
1135
- $[7] = t4;
1136
- } else t4 = $[7];
1137
- let t5;
1138
- if ($[8] !== t3) {
1139
- t5 = /* @__PURE__ */ jsxs("div", {
1140
- className: "flex items-center gap-2",
1141
- children: [t3, t4]
1142
- });
1143
- $[8] = t3;
1144
1146
  $[9] = t5;
1145
1147
  } else t5 = $[9];
1146
- const t6 = page.metaTitle ?? "";
1147
- let t7;
1148
- if ($[10] !== page.id || $[11] !== setSeo) {
1149
- t7 = (value) => setSeo.mutate({
1148
+ let t6;
1149
+ if ($[10] !== t4) {
1150
+ t6 = /* @__PURE__ */ jsxs("div", {
1151
+ className: "flex items-center gap-2",
1152
+ children: [t4, t5]
1153
+ });
1154
+ $[10] = t4;
1155
+ $[11] = t6;
1156
+ } else t6 = $[11];
1157
+ const t7 = page.metaTitle ?? "";
1158
+ let t8;
1159
+ if ($[12] !== page.id || $[13] !== setSeo) {
1160
+ t8 = (value) => setSeo.mutate({
1150
1161
  id: page.id,
1151
1162
  metaTitle: value
1152
1163
  });
1153
- $[10] = page.id;
1154
- $[11] = setSeo;
1155
- $[12] = t7;
1156
- } else t7 = $[12];
1157
- let t8;
1158
- if ($[13] !== page.aiSeo || $[14] !== t6 || $[15] !== t7) {
1159
- t8 = /* @__PURE__ */ jsx(DebouncedFieldEditor, {
1164
+ $[12] = page.id;
1165
+ $[13] = setSeo;
1166
+ $[14] = t8;
1167
+ } else t8 = $[14];
1168
+ let t9;
1169
+ if ($[15] !== page.aiSeo || $[16] !== t7 || $[17] !== t8) {
1170
+ t9 = /* @__PURE__ */ jsx(DebouncedFieldEditor, {
1160
1171
  label: "Page title",
1161
1172
  placeholder: "Page title...",
1162
- initialValue: t6,
1173
+ initialValue: t7,
1163
1174
  disabled: page.aiSeo,
1164
- onSave: t7
1175
+ onSave: t8
1165
1176
  });
1166
- $[13] = page.aiSeo;
1167
- $[14] = t6;
1168
- $[15] = t7;
1169
- $[16] = t8;
1170
- } else t8 = $[16];
1171
- const t9 = page.metaDescription ?? "";
1177
+ $[15] = page.aiSeo;
1178
+ $[16] = t7;
1179
+ $[17] = t8;
1180
+ $[18] = t9;
1181
+ } else t9 = $[18];
1172
1182
  let t10;
1173
- if ($[17] !== page.id || $[18] !== setSeo) {
1174
- t10 = (value_0) => setSeo.mutate({
1183
+ if ($[19] !== findings) {
1184
+ t10 = /* @__PURE__ */ jsx(ContentFindings, {
1185
+ view: findings,
1186
+ field: "metaTitle",
1187
+ className: "space-y-2"
1188
+ });
1189
+ $[19] = findings;
1190
+ $[20] = t10;
1191
+ } else t10 = $[20];
1192
+ const t11 = page.metaDescription ?? "";
1193
+ let t12;
1194
+ if ($[21] !== page.id || $[22] !== setSeo) {
1195
+ t12 = (value_0) => setSeo.mutate({
1175
1196
  id: page.id,
1176
1197
  metaDescription: value_0
1177
1198
  });
1178
- $[17] = page.id;
1179
- $[18] = setSeo;
1180
- $[19] = t10;
1181
- } else t10 = $[19];
1182
- let t11;
1183
- if ($[20] !== page.aiSeo || $[21] !== t10 || $[22] !== t9) {
1184
- t11 = /* @__PURE__ */ jsx(DebouncedFieldEditor, {
1199
+ $[21] = page.id;
1200
+ $[22] = setSeo;
1201
+ $[23] = t12;
1202
+ } else t12 = $[23];
1203
+ let t13;
1204
+ if ($[24] !== page.aiSeo || $[25] !== t11 || $[26] !== t12) {
1205
+ t13 = /* @__PURE__ */ jsx(DebouncedFieldEditor, {
1185
1206
  label: "Page description",
1186
1207
  placeholder: "Page description...",
1187
- initialValue: t9,
1208
+ initialValue: t11,
1188
1209
  disabled: page.aiSeo,
1189
1210
  rows: 2,
1190
- onSave: t10
1211
+ onSave: t12
1191
1212
  });
1192
- $[20] = page.aiSeo;
1193
- $[21] = t10;
1194
- $[22] = t9;
1195
- $[23] = t11;
1196
- } else t11 = $[23];
1197
- let t12;
1198
- if ($[24] !== localized || $[25] !== page.fullPath) {
1199
- t12 = localized(page.fullPath);
1200
- $[24] = localized;
1201
- $[25] = page.fullPath;
1213
+ $[24] = page.aiSeo;
1214
+ $[25] = t11;
1202
1215
  $[26] = t12;
1203
- } else t12 = $[26];
1204
- const t13 = page.metaDescription ?? "";
1216
+ $[27] = t13;
1217
+ } else t13 = $[27];
1205
1218
  let t14;
1206
- if ($[27] !== metaTitle || $[28] !== t12 || $[29] !== t13) {
1207
- t14 = /* @__PURE__ */ jsx(SearchEnginePreview, {
1208
- path: t12,
1209
- metaTitle,
1210
- metaDescription: t13
1211
- });
1212
- $[27] = metaTitle;
1213
- $[28] = t12;
1214
- $[29] = t13;
1219
+ if ($[28] !== localized || $[29] !== page.fullPath) {
1220
+ t14 = localized(page.fullPath);
1221
+ $[28] = localized;
1222
+ $[29] = page.fullPath;
1215
1223
  $[30] = t14;
1216
1224
  } else t14 = $[30];
1217
1225
  const t15 = page.metaDescription ?? "";
1218
1226
  let t16;
1219
- if ($[31] !== metaTitle || $[32] !== page || $[33] !== t15) {
1220
- t16 = /* @__PURE__ */ jsx(SocialPreviewSection, {
1221
- page,
1227
+ if ($[31] !== metaTitle || $[32] !== t14 || $[33] !== t15) {
1228
+ t16 = /* @__PURE__ */ jsx(SearchEnginePreview, {
1229
+ path: t14,
1222
1230
  metaTitle,
1223
1231
  metaDescription: t15
1224
1232
  });
1225
1233
  $[31] = metaTitle;
1226
- $[32] = page;
1234
+ $[32] = t14;
1227
1235
  $[33] = t15;
1228
1236
  $[34] = t16;
1229
1237
  } else t16 = $[34];
1230
- let t17;
1231
- if ($[35] !== t11 || $[36] !== t14 || $[37] !== t16 || $[38] !== t5 || $[39] !== t8) {
1232
- t17 = /* @__PURE__ */ jsxs("div", {
1238
+ const t17 = page.metaDescription ?? "";
1239
+ let t18;
1240
+ if ($[35] !== metaTitle || $[36] !== page || $[37] !== t17) {
1241
+ t18 = /* @__PURE__ */ jsx(SocialPreviewSection, {
1242
+ page,
1243
+ metaTitle,
1244
+ metaDescription: t17
1245
+ });
1246
+ $[35] = metaTitle;
1247
+ $[36] = page;
1248
+ $[37] = t17;
1249
+ $[38] = t18;
1250
+ } else t18 = $[38];
1251
+ let t19;
1252
+ if ($[39] !== t10 || $[40] !== t13 || $[41] !== t16 || $[42] !== t18 || $[43] !== t6 || $[44] !== t9) {
1253
+ t19 = /* @__PURE__ */ jsxs("div", {
1233
1254
  className: "space-y-4",
1234
1255
  children: [
1235
- t5,
1236
- t8,
1237
- t11,
1238
- t14,
1239
- t16
1256
+ t6,
1257
+ t9,
1258
+ t10,
1259
+ t13,
1260
+ t16,
1261
+ t18
1240
1262
  ]
1241
1263
  });
1242
- $[35] = t11;
1243
- $[36] = t14;
1244
- $[37] = t16;
1245
- $[38] = t5;
1246
- $[39] = t8;
1247
- $[40] = t17;
1248
- } else t17 = $[40];
1249
- return t17;
1264
+ $[39] = t10;
1265
+ $[40] = t13;
1266
+ $[41] = t16;
1267
+ $[42] = t18;
1268
+ $[43] = t6;
1269
+ $[44] = t9;
1270
+ $[45] = t19;
1271
+ } else t19 = $[45];
1272
+ return t19;
1250
1273
  };
1251
1274
  const PageMarkdownSheet = (t0) => {
1252
1275
  const $ = c(8);
@@ -10,8 +10,8 @@ import { useEditingFrame } from "../editingFrame.js";
10
10
  import { SelectionViewCount, SidebarStackHeader, SidebarStackView } from "./SidebarStack.js";
11
11
  import { PreviewEditingOwnerContext } from "../previewSelection.js";
12
12
  import { entityName } from "../entityName.js";
13
- import { fieldTypesDictionary } from "../../../core/lib/fieldTypes.js";
14
13
  import { ContentFindings, findingsHint, useContentFindings } from "../../studio/components/ContentFindings.js";
14
+ import { fieldTypesDictionary } from "../../../core/lib/fieldTypes.js";
15
15
  import { usePageRecordPlacer } from "../pageRecord.js";
16
16
  import { contentFieldNames, contentFieldSchema } from "./contentFieldSchema.js";
17
17
  import { useRepeatableItemActions } from "./useRepeatableItemActions.js";
@@ -103,7 +103,7 @@ function _temp() {}
103
103
  * the studio keeps the Canvas position and selection, and tells the new renderer.
104
104
  */
105
105
  function RendererFramesProvider(t0) {
106
- const $ = c(33);
106
+ const $ = c(34);
107
107
  const { runtimeBasePath, children } = t0;
108
108
  const queryClient = useQueryClient();
109
109
  const studioCache = useStudioCache();
@@ -147,6 +147,7 @@ function RendererFramesProvider(t0) {
147
147
  const documentUrl = t3;
148
148
  const [source, setSource] = React.useState(documentUrl);
149
149
  const loaded = React.useRef(source);
150
+ const [loads, setLoads] = React.useState(0);
150
151
  let t4;
151
152
  if ($[8] !== documentUrl || $[9] !== host) {
152
153
  t4 = (t5) => {
@@ -156,6 +157,7 @@ function RendererFramesProvider(t0) {
156
157
  loaded.current = next;
157
158
  host?.replacing();
158
159
  setSource(next);
160
+ setLoads(_temp2);
159
161
  };
160
162
  $[8] = documentUrl;
161
163
  $[9] = host;
@@ -164,23 +166,29 @@ function RendererFramesProvider(t0) {
164
166
  const reload = t4;
165
167
  let t5;
166
168
  let t6;
167
- if ($[11] !== draftScope || $[12] !== reload) {
169
+ if ($[11] !== documentUrl || $[12] !== draftScope || $[13] !== reload) {
168
170
  t5 = () => draftScope.subscribe((t7) => {
169
171
  const { move } = t7;
170
- if (move !== "started") reload();
172
+ if (move === "started") loaded.current = documentUrl();
173
+ else reload();
171
174
  });
172
- t6 = [draftScope, reload];
173
- $[11] = draftScope;
174
- $[12] = reload;
175
- $[13] = t5;
176
- $[14] = t6;
175
+ t6 = [
176
+ draftScope,
177
+ documentUrl,
178
+ reload
179
+ ];
180
+ $[11] = documentUrl;
181
+ $[12] = draftScope;
182
+ $[13] = reload;
183
+ $[14] = t5;
184
+ $[15] = t6;
177
185
  } else {
178
- t5 = $[13];
179
- t6 = $[14];
186
+ t5 = $[14];
187
+ t6 = $[15];
180
188
  }
181
189
  React.useEffect(t5, t6);
182
190
  let t7;
183
- if ($[15] !== host) {
191
+ if ($[16] !== host) {
184
192
  t7 = (next_0) => {
185
193
  if (!host) return Promise.resolve();
186
194
  return host.ask({
@@ -190,47 +198,45 @@ function RendererFramesProvider(t0) {
190
198
  locale: next_0.locale
191
199
  });
192
200
  };
193
- $[15] = host;
194
- $[16] = t7;
195
- } else t7 = $[16];
201
+ $[16] = host;
202
+ $[17] = t7;
203
+ } else t7 = $[17];
196
204
  useViewPreparer(t7);
197
- const [loads, setLoads] = React.useState(0);
198
205
  const revisions = React.use(RevisionsContext);
199
206
  let t8;
200
207
  let t9;
201
- if ($[17] !== draftScope || $[18] !== reload || $[19] !== revisions) {
208
+ if ($[18] !== draftScope || $[19] !== reload || $[20] !== revisions) {
202
209
  t8 = () => revisions?.subscribe((t10) => {
203
210
  const { draftId } = t10;
204
211
  if (draftId !== draftScope.current()) return;
205
212
  reload(true);
206
- setLoads(_temp2);
207
213
  });
208
214
  t9 = [
209
215
  revisions,
210
216
  draftScope,
211
217
  reload
212
218
  ];
213
- $[17] = draftScope;
214
- $[18] = reload;
215
- $[19] = revisions;
216
- $[20] = t8;
217
- $[21] = t9;
219
+ $[18] = draftScope;
220
+ $[19] = reload;
221
+ $[20] = revisions;
222
+ $[21] = t8;
223
+ $[22] = t9;
218
224
  } else {
219
- t8 = $[20];
220
- t9 = $[21];
225
+ t8 = $[21];
226
+ t9 = $[22];
221
227
  }
222
228
  React.useEffect(t8, t9);
223
229
  let t10;
224
- if ($[22] !== host) {
230
+ if ($[23] !== host) {
225
231
  t10 = (frame) => {
226
232
  if (frame && host) frame[STUDIO_HANDLE_KEY] = host.handle;
227
233
  };
228
- $[22] = host;
229
- $[23] = t10;
230
- } else t10 = $[23];
234
+ $[23] = host;
235
+ $[24] = t10;
236
+ } else t10 = $[24];
231
237
  const connect = t10;
232
238
  let t11;
233
- if ($[24] !== connect || $[25] !== host || $[26] !== loads || $[27] !== source) {
239
+ if ($[25] !== connect || $[26] !== host || $[27] !== loads || $[28] !== source) {
234
240
  t11 = host && /* @__PURE__ */ jsx("iframe", {
235
241
  ref: connect,
236
242
  src: source,
@@ -238,23 +244,23 @@ function RendererFramesProvider(t0) {
238
244
  title: "Website renderer",
239
245
  "data-camox-renderer": true
240
246
  }, `${source}#${loads}`);
241
- $[24] = connect;
242
- $[25] = host;
243
- $[26] = loads;
244
- $[27] = source;
245
- $[28] = t11;
246
- } else t11 = $[28];
247
+ $[25] = connect;
248
+ $[26] = host;
249
+ $[27] = loads;
250
+ $[28] = source;
251
+ $[29] = t11;
252
+ } else t11 = $[29];
247
253
  let t12;
248
- if ($[29] !== children || $[30] !== host || $[31] !== t11) {
254
+ if ($[30] !== children || $[31] !== host || $[32] !== t11) {
249
255
  t12 = /* @__PURE__ */ jsxs(RendererHostContext, {
250
256
  value: host,
251
257
  children: [children, t11]
252
258
  });
253
- $[29] = children;
254
- $[30] = host;
255
- $[31] = t11;
256
- $[32] = t12;
257
- } else t12 = $[32];
259
+ $[30] = children;
260
+ $[31] = host;
261
+ $[32] = t11;
262
+ $[33] = t12;
263
+ } else t12 = $[33];
258
264
  return t12;
259
265
  }
260
266
  function _temp2(count) {
@@ -157,13 +157,24 @@ function createServerStudioCache(queryClient, { apiUrl, authCookieHeader, lookTo
157
157
  }
158
158
  /**
159
159
  * The locales of the scope a request reads: the URL's draft's for a session naming one, else
160
- * Live's. A draft the API refuses reads Live's, as the request's other reads then do.
160
+ * Live's. A draft the API refuses reads Live's, as the request's other reads then do. Live's read
161
+ * keeps the session: a member's studio routes the locales Live doesn't serve yet, which visitors
162
+ * never see (ADR 0043), and a render with a session is private.
161
163
  */
162
164
  async function readScopeLocales({ apiUrl, authCookieHeader, draftId, projectUuid }) {
163
- const { value } = await readDraftOrLive(draftId, !!authCookieHeader, (readDraftId) => createServerApiClient(apiUrl, {
164
- authCookieHeader: readDraftId ? authCookieHeader : void 0,
165
+ const list = (session, readDraftId) => createServerApiClient(apiUrl, {
166
+ authCookieHeader: session,
165
167
  draftId: readDraftId
166
- }).locales.list({ projectUuid }));
168
+ }).locales.list({ projectUuid });
169
+ const { value } = await readDraftOrLive(draftId, !!authCookieHeader, async (readDraftId) => {
170
+ if (readDraftId || !authCookieHeader) return list(authCookieHeader, readDraftId);
171
+ try {
172
+ return await list(authCookieHeader, void 0);
173
+ } catch (error) {
174
+ if (!isAuthSessionError(error)) throw error;
175
+ return list(void 0, void 0);
176
+ }
177
+ });
167
178
  return value;
168
179
  }
169
180
  /**