actually-written 0.1.0 → 0.1.1

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,52 +1,25 @@
1
- type Evidence = {
2
- revisionCount: number;
3
- typedWordCount: number;
4
- pasteCount: number;
5
- pastedWordCount: number;
6
- largestPasteWordCount: number;
7
- dictationCount?: number;
8
- dictatedWordCount?: number;
9
- dictationSeconds?: number;
10
- pauseCount: number;
11
- activeWritingSeconds: number;
12
- idleSeconds: number;
13
- };
14
- type InitialActivity = {
15
- revisionCount?: number;
16
- evidence?: Partial<Evidence>;
17
- };
18
- type History = {
19
- revisionCount: number;
20
- evidence: Evidence;
21
- };
22
- export declare const createWritingActivity: (initial?: InitialActivity) => {
1
+ export declare const createWritingActivity: () => {
23
2
  type: (text: string) => void;
24
- backspace: (isRevisionCounted: boolean) => void;
3
+ backspace: () => void;
25
4
  copy: (text: string) => void;
26
- paste: (text: string) => true | undefined;
27
- confirmDictation: (text: string, seconds: number) => void;
28
- dictate: (text: string, startedAt: number) => void;
29
- stop: () => void;
5
+ paste: (text: string) => void;
6
+ insertUnkeyed: (text: string) => void;
30
7
  endRevision: () => void;
31
- read: (body: string) => {
32
- wordCount: number;
8
+ stop: () => void;
9
+ read: () => {
33
10
  revisionCount: number;
34
11
  evidence: {
35
- activeWritingSeconds: number;
36
- idleSeconds: number;
37
- dictationCount?: number | undefined;
38
- dictatedWordCount?: number | undefined;
39
- dictationSeconds?: number | undefined;
40
12
  revisionCount: number;
41
13
  typedWordCount: number;
42
14
  pasteCount: number;
43
15
  pastedWordCount: number;
44
16
  largestPasteWordCount: number;
45
17
  pauseCount: number;
18
+ unkeyedWordCount: number;
19
+ keystrokeCount: number;
20
+ keystrokeIntervalVariation: number;
21
+ activeWritingSeconds: number;
22
+ idleSeconds: number;
46
23
  };
47
24
  };
48
- include: (history: History, isRevisionCounted: boolean) => void;
49
- clearRevisions: () => void;
50
- readonly detectedWords: number;
51
25
  };
52
- export {};
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- var v=e=>e.match(/\S+/gu)?.length||0,W=e=>e.replace(/\s+/gu," ").trim(),A=(e={})=>{let i=e.evidence||{},s={lastInputAt:null,pauseTimer:null},t={revisionCount:e.revisionCount||0,isBackspacing:!1,detectedWords:0,isInWord:!1,hasRecordedPause:!1,typedWordCount:i.typedWordCount||0,pasteCount:i.pasteCount||0,privateRevisionCount:i.revisionCount??e.revisionCount??0,pastedWordCount:i.pastedWordCount||0,largestPasteWordCount:i.largestPasteWordCount||0,dictationCount:i.dictationCount||0,dictatedWordCount:i.dictatedWordCount||0,dictationSeconds:i.dictationSeconds||0,copiedTexts:new Set,pauseCount:i.pauseCount||0,activeWritingSeconds:i.activeWritingSeconds||0,idleSeconds:i.idleSeconds||0},a=()=>{s.lastInputAt===null||t.hasRecordedPause||Date.now()-s.lastInputAt<7*1e3||(t.pauseCount++,t.hasRecordedPause=!0)},d=()=>{s.pauseTimer!==null&&clearTimeout(s.pauseTimer)},S=()=>{a(),d(),s.pauseTimer=null,s.lastInputAt=null,t.isBackspacing=!1},l=(n=Date.now())=>{a(),d();let r=s.lastInputAt===null?0:Math.max(0,n-s.lastInputAt)/1e3;r>=7?t.idleSeconds+=Math.min(r,1800):t.activeWritingSeconds+=r,s.lastInputAt=n,t.hasRecordedPause=!1,s.pauseTimer=setTimeout(a,7*1e3)};return{type:n=>{t.isBackspacing=!1,l();let r=Math.max(0,v(n)-(t.isInWord&&/^\S/u.test(n)?1:0));t.detectedWords+=r,t.typedWordCount+=r,t.isInWord=/\S$/u.test(n)},backspace:n=>{l(),!t.isBackspacing&&(t.isBackspacing=!0,t.privateRevisionCount++,n&&t.revisionCount++)},copy:n=>{W(n)&&t.copiedTexts.add(W(n))},paste:n=>{if(t.isBackspacing=!1,t.isInWord=!1,t.copiedTexts.has(W(n)))return;l();let r=v(n);return t.pasteCount++,t.pastedWordCount+=r,t.largestPasteWordCount=Math.max(t.largestPasteWordCount,r),!0},confirmDictation:(n,r)=>{let p=v(n);t.pasteCount=Math.max(0,t.pasteCount-1),t.pastedWordCount=Math.max(0,t.pastedWordCount-p),t.dictationCount++,t.dictatedWordCount+=p,t.dictationSeconds+=r,t.activeWritingSeconds+=r,t.detectedWords+=p},dictate:(n,r)=>{t.isBackspacing=!1,t.isInWord=!1;let p=Date.now(),y=Math.min(p,Math.max(r,s.lastInputAt??r));l(y);let m=(p-y)/1e3;t.activeWritingSeconds+=m,t.dictationSeconds+=m,s.lastInputAt=p;let E=v(n);t.dictationCount++,t.dictatedWordCount+=E,t.detectedWords+=E},stop:S,endRevision:()=>{t.isBackspacing=!1},read:n=>(a(),{wordCount:v(n),revisionCount:t.revisionCount,evidence:{revisionCount:t.privateRevisionCount,typedWordCount:t.typedWordCount,pasteCount:t.pasteCount,pastedWordCount:t.pastedWordCount,largestPasteWordCount:t.largestPasteWordCount,pauseCount:t.pauseCount,...t.dictationCount?{dictationCount:t.dictationCount,dictatedWordCount:t.dictatedWordCount,dictationSeconds:Math.round(t.dictationSeconds)}:{},activeWritingSeconds:Math.round(t.activeWritingSeconds),idleSeconds:Math.round(t.idleSeconds)}}),include:(n,r)=>{t.typedWordCount+=n.evidence.typedWordCount,t.pasteCount+=n.evidence.pasteCount,t.pastedWordCount+=n.evidence.pastedWordCount,t.largestPasteWordCount=Math.max(t.largestPasteWordCount,n.evidence.largestPasteWordCount),t.pauseCount+=n.evidence.pauseCount,t.activeWritingSeconds+=n.evidence.activeWritingSeconds,t.idleSeconds+=n.evidence.idleSeconds,t.privateRevisionCount+=n.evidence.revisionCount,r&&(t.revisionCount+=n.revisionCount)},clearRevisions:()=>{t.revisionCount=0},get detectedWords(){return t.detectedWords}}};var T=(e,i,s)=>{if(i>=s)return i;let t=Math.ceil((i+s)/2);return e(t)?T(e,t,s):T(e,i,t-1)},w=(e,i)=>{let s=Math.min(e.length,i.length),t=T(d=>e.slice(0,d)===i.slice(0,d),0,s),a=T(d=>e.slice(e.length-d)===i.slice(i.length-d),0,s-t);return{position:t,deleted:e.slice(t,e.length-a),inserted:i.slice(t,i.length-a)}};var P=".actually-written-badge",D=`
1
+ var C=t=>t.match(/\S+/gu)?.length||0,k=t=>t.replace(/\s+/gu," ").trim(),D=()=>{let t={lastInputAt:null,lastKeystrokeAt:null,pauseTimer:null},s={count:0,mean:0,squaredDeviation:0},e={revisionCount:0,isBackspacing:!1,isInWord:!1,hasRecordedPause:!1,typedWordCount:0,pasteCount:0,pastedWordCount:0,unkeyedWordCount:0,largestPasteWordCount:0,pauseCount:0,activeWritingSeconds:0,idleSeconds:0,copiedTexts:new Set},r=()=>{t.lastInputAt===null||e.hasRecordedPause||Date.now()-t.lastInputAt<7*1e3||(e.pauseCount++,e.hasRecordedPause=!0)},a=()=>{t.pauseTimer!==null&&clearTimeout(t.pauseTimer)},l=()=>{let o=Date.now();r(),a();let i=t.lastInputAt===null?0:Math.max(0,o-t.lastInputAt)/1e3;i>=7?e.idleSeconds+=Math.min(i,1800):e.activeWritingSeconds+=i,t.lastInputAt=o,e.hasRecordedPause=!1,t.pauseTimer=setTimeout(r,7*1e3)},g=()=>{let o=Date.now(),i=t.lastKeystrokeAt===null?null:o-t.lastKeystrokeAt;if(t.lastKeystrokeAt=o,i===null||i>1e3)return;s.count++;let E=i-s.mean;s.mean+=E/s.count,s.squaredDeviation+=E*(i-s.mean)},u=()=>s.count<2||s.mean<=0?0:Math.round(Math.sqrt(s.squaredDeviation/(s.count-1))/s.mean*1e3)/1e3;return{type:o=>{e.isBackspacing=!1,l(),o.length===1&&g(),e.typedWordCount+=Math.max(0,C(o)-(e.isInWord&&/^\S/u.test(o)?1:0)),e.isInWord=/\S$/u.test(o)},backspace:()=>{l(),!e.isBackspacing&&(e.isBackspacing=!0,e.revisionCount++)},copy:o=>{k(o)&&e.copiedTexts.add(k(o))},paste:o=>{if(e.isBackspacing=!1,e.isInWord=!1,e.copiedTexts.has(k(o)))return;l();let i=C(o);e.pasteCount++,e.pastedWordCount+=i,e.largestPasteWordCount=Math.max(e.largestPasteWordCount,i)},insertUnkeyed:o=>{e.isBackspacing=!1,e.isInWord=!1,l(),e.unkeyedWordCount+=C(o)},endRevision:()=>{e.isBackspacing=!1},stop:()=>{r(),a()},read:()=>(r(),{revisionCount:e.revisionCount,evidence:{revisionCount:e.revisionCount,typedWordCount:e.typedWordCount,pasteCount:e.pasteCount,pastedWordCount:e.pastedWordCount,largestPasteWordCount:e.largestPasteWordCount,pauseCount:e.pauseCount,unkeyedWordCount:e.unkeyedWordCount,keystrokeCount:s.count,keystrokeIntervalVariation:u(),activeWritingSeconds:Math.round(e.activeWritingSeconds),idleSeconds:Math.round(e.idleSeconds)}})}};var S=(t,s,e)=>{if(s>=e)return s;let r=Math.ceil((s+e)/2);return t(r)?S(t,r,e):S(t,s,r-1)},W=(t,s)=>{let e=Math.min(t.length,s.length),r=S(l=>t.slice(0,l)===s.slice(0,l),0,e),a=S(l=>t.slice(t.length-l)===s.slice(s.length-l),0,e-r);return{position:r,deleted:t.slice(r,t.length-a),inserted:s.slice(r,s.length-a)}};var N=".actually-written-badge",O=`
2
2
  a {
3
3
  display: inline-flex;
4
4
  align-items: center;
@@ -46,7 +46,5 @@ a:focus-visible .label {
46
46
  [data-state="off"] .state {
47
47
  background: #c00;
48
48
  }
49
- `,H=e=>{switch(e){case"recording":return{status:"Recording how this form is written",mark:"\u2713"};case"off":return{status:"Not recording",mark:"\u2715"};default:return e}},_=(e,i)=>{let s=e.shadowRoot??e.attachShadow({mode:"open"});s.innerHTML=`<style>${D}</style><a target="_blank" rel="noopener"><img alt=""><span class="label"><strong>Actually Written</strong><span class="status"></span></span><span class="state" aria-hidden="true"></span></a>`;let t=s.querySelector("a"),a=s.querySelector("img"),d=s.querySelector(".status"),S=s.querySelector(".state");if(!t||!a||!d||!S)throw new Error("Actually Written could not render its badge.");t.href=new URL("/process",i).href,a.src=new URL("/favicon.svg",i).href;let l=n=>{let r=H(n);t.dataset.state=n,t.setAttribute("aria-label",`Actually Written: ${r.status}`),d.textContent=r.status,S.textContent=r.mark};return l("recording"),{setState:l,remove:()=>s.replaceChildren()}},M=(e,i)=>{let s=[...e.querySelectorAll(P)].map(t=>_(t,i));return{setState:t=>s.forEach(a=>a.setState(t)),remove:()=>s.forEach(t=>t.remove())}};var I='textarea, [contenteditable=""], [contenteditable="true"], input:not([type]), input[type="text"], input[type="search"], input[type="url"]',B="actually_written_session",R="https://actuallywritten.com",O=6e4,N=["copy","cut","dragstart"],U=["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home","End","PageUp","PageDown"],k=e=>e instanceof HTMLElement&&e.matches(I),b=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement?e.value:e.innerText,F=e=>e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement?e.value.slice(e.selectionStart??0,e.selectionEnd??0):e.ownerDocument.getSelection()?.toString()??"",q=e=>e instanceof InputEvent&&/^insertFrom(?:Paste|Drop)$/u.test(e.inputType),$=e=>e instanceof HTMLFormElement?e:e.closest("form")??e.querySelector("form"),J=(e,{key:i,endpoint:s=R})=>{if(!e)throw new Error("Actually Written needs a form element to track.");let t=crypto.randomUUID(),a=A(),d=new AbortController,S={capture:!0,signal:d.signal},l={startedAt:null},n=new Map,r=$(e),p=Object.assign(document.createElement("input"),{type:"hidden",name:B,value:t});r?.append(p);let y=M(r??e,s),m=()=>{l.startedAt??(l.startedAt=Date.now())},E=o=>{let u=n.get(o);if(u)return u;let c=b(o),C={initialText:c,text:c,edits:[]};return n.set(o,C),C},L=()=>[e,...e.querySelectorAll(I)].filter(k).map((o,u)=>{let c=n.get(o);return{name:o.getAttribute("name")||o.id||`field-${u+1}`,text:b(o),...c?.edits.length?{initialText:c.initialText,edits:c.edits}:{}}}).filter(o=>o.text.trim()),g=(o,u)=>e.addEventListener(o,c=>{c.isTrusted&&k(c.target)&&u(c,c.target)},S);g("keydown",o=>{if(U.includes(o.key)&&a.endRevision(),o.key==="Backspace"){m(),a.backspace(!0);return}o.isComposing||o.ctrlKey||o.metaKey||o.altKey||(o.key.length===1||o.key==="Enter")&&(m(),a.type(o.key==="Enter"?`
50
- `:o.key))}),g("compositionend",o=>{m(),a.type(o.data)}),g("pointerdown",()=>a.endRevision()),N.forEach(o=>g(o,(u,c)=>a.copy(F(c)))),g("paste",o=>{m(),a.paste(o.clipboardData?.getData("text/plain")??"")}),g("drop",o=>{m(),a.paste(o.dataTransfer?.getData("text/plain")??"")}),g("beforeinput",(o,u)=>E(u)),g("input",(o,u)=>{let c=E(u),C=b(u);C!==c.text&&(c.edits.push({at:Date.now(),...w(c.text,C),source:q(o)?"pasted":"typed"}),c.text=C)});let h=async()=>{let o=L(),{revisionCount:u,evidence:c}=a.read(o.map(f=>f.text).join(`
51
-
52
- `)),C=l.startedAt===null?0:Math.round((Date.now()-l.startedAt)/1e3),x=JSON.stringify({key:i,sessionId:t,fields:o,metrics:{revisionCount:u,writingTimeSeconds:C,evidence:c}});try{let f=await fetch(new URL("/api/submissions",s),{method:"POST",headers:{"Content-Type":"text/plain"},body:x,keepalive:new Blob([x]).size<=O});if(f.ok)return;y.setState("off"),console.warn(`Actually Written could not record this submission (HTTP ${f.status}).`)}catch(f){y.setState("off"),console.warn("Actually Written could not record this submission.",f)}};return(r??e).addEventListener("submit",()=>{h()},S),{id:t,submit:h,stop:()=>{d.abort(),a.stop(),p.remove(),y.remove()}}};export{J as track};
49
+ `,U=t=>{switch(t){case"recording":return{status:"Recording how this form is written",mark:"\u2713"};case"off":return{status:"Not recording",mark:"\u2715"};default:return t}},B=(t,s)=>{let e=t.shadowRoot??t.attachShadow({mode:"open"});e.innerHTML=`<style>${O}</style><a target="_blank" rel="noopener"><img alt=""><span class="label"><strong>Actually Written</strong><span class="status"></span></span><span class="state" aria-hidden="true"></span></a>`;let r=e.querySelector("a"),a=e.querySelector("img"),l=e.querySelector(".status"),g=e.querySelector(".state");if(!r||!a||!l||!g)throw new Error("Actually Written could not render its badge.");r.href=new URL("/process",s).href,a.src=new URL("/favicon.svg",s).href;let u=o=>{let i=U(o);r.dataset.state=o,r.setAttribute("aria-label",`Actually Written: ${i.status}`),l.textContent=i.status,g.textContent=i.mark};return u("recording"),{setState:u,remove:()=>e.replaceChildren()}},_=(t,s)=>{let e=[...t.querySelectorAll(N)].map(r=>B(r,s));return{setState:r=>e.forEach(a=>a.setState(r)),remove:()=>e.forEach(r=>r.remove())}};var P='textarea, [contenteditable=""], [contenteditable="true"], input:not([type]), input[type="text"], input[type="search"], input[type="url"]',R="actually_written_session",K="https://actuallywritten.com",F=6e4,q=1e3,V=["copy","cut","dragstart"],X=100,Y=200,$=["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home","End","PageUp","PageDown"],b=t=>t instanceof HTMLElement&&t.matches(P),x=t=>t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement?t.value:t.innerText,G=t=>t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement?t.value.slice(t.selectionStart??0,t.selectionEnd??0):t.ownerDocument.getSelection()?.toString()??"",v=t=>t instanceof InputEvent&&/^insertFrom(?:Paste|Drop)$/u.test(t.inputType),z=t=>!(t instanceof InputEvent)||t.inputType.startsWith("insert")&&t.inputType!=="insertCompositionText"&&!v(t),j=t=>CSS.supports("selector(:autofill)")&&t.matches(":autofill"),J=t=>t instanceof HTMLFormElement?t:t.closest("form")??t.querySelector("form"),ot=(t,{key:s,endpoint:e=K})=>{if(!t)throw new Error("Actually Written needs a form element to track.");let r=crypto.randomUUID(),a=D(),l=new AbortController,g={capture:!0,signal:l.signal},u={startedAt:null},o=new Map,i={lastPressedAt:null},E=J(t),I=Object.assign(document.createElement("input"),{type:"hidden",name:R,value:r});E?.append(I);let h=_(E??t,e),f=()=>{u.startedAt??(u.startedAt=Date.now())},A=n=>{let d=o.get(n);if(d)return d;let c=x(n),p={initialText:c,text:c,edits:[]};return o.set(n,p),p},M=()=>[t,...t.querySelectorAll(P)].filter(b),w=(n,d,c)=>{let p=A(n),T=x(n);if(T===p.text)return;let m=W(p.text,T);p.edits.push({at:Date.now(),...m,source:d}),p.text=T,!(!c||j(n))&&(f(),a.insertUnkeyed(m.inserted))};M().forEach(A);let H=()=>M().map((n,d)=>{let c=o.get(n);return{name:(n.getAttribute("name")||n.id||`field-${d+1}`).slice(0,Y),text:x(n),...c?.edits.length?{initialText:c.initialText,edits:c.edits}:{}}}).filter(n=>n.text.trim()).slice(0,X),y=(n,d)=>t.addEventListener(n,c=>{c.isTrusted&&b(c.target)&&d(c,c.target)},g);y("keydown",n=>{if(i.lastPressedAt=Date.now(),$.includes(n.key)&&a.endRevision(),n.key==="Backspace"){f(),a.backspace();return}n.isComposing||n.ctrlKey||n.metaKey||n.altKey||(n.key.length===1||n.key==="Enter")&&(f(),a.type(n.key==="Enter"?`
50
+ `:n.key))}),y("compositionend",n=>{f(),a.type(n.data)}),y("pointerdown",()=>a.endRevision()),V.forEach(n=>y(n,(d,c)=>a.copy(G(c)))),y("paste",n=>{f(),a.paste(n.clipboardData?.getData("text/plain")??"")}),y("drop",n=>{f(),a.paste(n.dataTransfer?.getData("text/plain")??"")}),y("beforeinput",(n,d)=>A(d)),t.addEventListener("input",n=>{if(!b(n.target))return;let d=n.isTrusted&&i.lastPressedAt!==null&&Date.now()-i.lastPressedAt<=q;i.lastPressedAt=null,w(n.target,v(n)?"pasted":"typed",!d&&z(n))},g);let L=async()=>{M().forEach(m=>w(m,"typed",!0));let n=H(),{revisionCount:d,evidence:c}=a.read(),p=u.startedAt===null?0:Math.round((Date.now()-u.startedAt)/1e3),T=JSON.stringify({key:s,sessionId:r,fields:n,metrics:{revisionCount:d,writingTimeSeconds:p,evidence:c}});try{let m=await fetch(new URL("/api/submissions",e),{method:"POST",headers:{"Content-Type":"text/plain"},body:T,keepalive:new Blob([T]).size<=F});if(m.ok)return;h.setState("off"),console.warn(`Actually Written could not record this submission (HTTP ${m.status}).`)}catch(m){h.setState("off"),console.warn("Actually Written could not record this submission.",m)}};return(E??t).addEventListener("submit",()=>{L()},g),{id:r,submit:L,stop:()=>{l.abort(),a.stop(),I.remove(),h.remove()}}};export{ot as track};
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "actually-written",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Record how answers in your forms were written, then review each submission's writing evidence.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://actuallywritten.com/docs",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/thecontextcompany/pasta.git",
9
+ "url": "git+https://github.com/nickkkk1234/actually-written.git",
10
10
  "directory": "packages/sdk"
11
11
  },
12
12
  "type": "module",