@picked/svelte 0.0.1-dev.1793772853474 → 0.0.1-dev.1793776638554
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/dist/.tsbuildinfo +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +46 -13
- package/dist/plugins/devtools/components/AdaptedToolHost.svelte +9 -3
- package/dist/plugins/devtools/customTools.d.ts +16 -7
- package/dist/plugins/devtools/customTools.js +1 -1
- package/dist/plugins/devtools/derive/answer-display/answer-display.d.ts +1 -1
- package/dist/plugins/devtools/derive/answer-display/answer-display.js +1 -1
- package/dist/plugins/devtools/derive/guide-sections/guide-sections.js +1 -1
- package/dist/plugins/devtools/derive/journey-outline/journey-outline.js +1 -1
- package/dist/plugins/devtools/derive/journey-tree/journey-tree.js +1 -1
- package/dist/plugins/devtools/derive/persona-builder/persona-builder.js +1 -1
- package/dist/plugins/devtools/derive/personas/personas.d.ts +1 -1
- package/dist/plugins/devtools/derive/personas/personas.js +1 -1
- package/dist/plugins/devtools/derive/plain-language/plain-language.d.ts +20 -23
- package/dist/plugins/devtools/derive/plain-language/plain-language.js +1 -1
- package/dist/plugins/devtools/derive/plain-logic/plain-logic.d.ts +1 -1
- package/dist/plugins/devtools/derive/plain-logic/plain-logic.js +1 -1
- package/dist/plugins/devtools/derive/safety-posture/safety-posture.js +1 -1
- package/dist/plugins/devtools/options.d.ts +22 -21
- package/dist/plugins/devtools/options.js +1 -1
- package/dist/plugins/devtools/review/answers/AnswersPanel.svelte +14 -14
- package/dist/plugins/devtools/review/guide/GuidePanel.svelte +1 -1
- package/dist/plugins/devtools/review/index.d.ts +2 -2
- package/dist/plugins/devtools/review/journey/JourneyMap.svelte +16 -14
- package/dist/plugins/devtools/review/journey/JourneyPanel.svelte +20 -20
- package/dist/plugins/devtools/review/journey/JourneyTimeline.svelte +22 -21
- package/dist/plugins/devtools/review/personas/PersonaBuilder.svelte +12 -12
- package/dist/plugins/devtools/review/personas/PersonaCard.svelte +1 -1
- package/dist/plugins/devtools/review/personas/PersonasPanel.svelte +25 -9
- package/dist/plugins/devtools/review/personas/load-persona.d.ts +2 -2
- package/dist/plugins/devtools/review/personas/load-persona.js +1 -1
- package/dist/plugins/devtools/review/session/SessionPanel.svelte +11 -11
- package/dist/plugins/devtools/review/variations/VariationsPanel.svelte +12 -13
- package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +11 -25
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +2 -2
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +3 -3
- package/dist/plugins/devtools/sidebar/Sidebar.svelte +38 -4
- package/dist/plugins/devtools/sidebar/nav.d.ts +32 -8
- package/dist/plugins/devtools/sidebar/nav.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NODE_LABELS as e,deslug as t,screenName as n}from"../plain-language/plain-language.js";import{describeRule as r}from"../plain-logic/plain-logic.js";var i=88,a=220,o=110,s=56,c=2e3,l=new Map([[`true`,{arm:`yes`,label:`Yes`,order:0}],[`false`,{arm:`no`,label:`Otherwise`,order:1}]]),u={kind:`system`,label:e.action,detail:null,rule:null,stepKey:null};function d(e,n,i){let a=e.conditionId===void 0?void 0:n.get(e.conditionId),o=r(a?.logic,i),s=a===void 0?o:t(a.name);return{kind:`decision`,label:o===`a rule this panel can't put into words`?s:o,detail:a?.name??null,rule:JSON.stringify(a?.logic??null,null,2),stepKey:null}}function f(r,i,a){if(r.type===`origin`)return{kind:`start`,label:e.origin,detail:null,rule:null,stepKey:null};if(r.type===`step`)return{kind:`screen`,label:n(r.name,r.stepKey),detail:r.stepKey??null,rule:null,stepKey:r.stepKey??null};if(r.type===`condition`)return d(r,i,a);if(r.type===`experience`){let e=r.experienceKey??`another
|
|
1
|
+
import{NODE_LABELS as e,deslug as t,screenName as n}from"../plain-language/plain-language.js";import{describeRule as r}from"../plain-logic/plain-logic.js";var i=88,a=220,o=110,s=56,c=2e3,l=new Map([[`true`,{arm:`yes`,label:`Yes`,order:0}],[`false`,{arm:`no`,label:`Otherwise`,order:1}]]),u={kind:`system`,label:e.action,detail:null,rule:null,stepKey:null};function d(e,n,i){let a=e.conditionId===void 0?void 0:n.get(e.conditionId),o=r(a?.logic,i),s=a===void 0?o:t(a.name);return{kind:`decision`,label:o===`a rule this panel can't put into words`?s:o,detail:a?.name??null,rule:JSON.stringify(a?.logic??null,null,2),stepKey:null}}function f(r,i,a){if(r.type===`origin`)return{kind:`start`,label:e.origin,detail:null,rule:null,stepKey:null};if(r.type===`step`)return{kind:`screen`,label:n(r.name,r.stepKey),detail:r.stepKey??null,rule:null,stepKey:r.stepKey??null};if(r.type===`condition`)return d(r,i,a);if(r.type===`experience`){let e=r.experienceKey??`another experience`;return{kind:`journey`,label:t(e),detail:e,rule:null,stepKey:null}}let o=r.actionKey??r.name??r.type;return{kind:`system`,label:t(o),detail:o,rule:null,stepKey:null}}function p(e,t,n){return e!==null&&e===n?`current`:e!==null&&t.has(e)?`visited`:`ahead`}function m(e,t,n){let r=new Set,i=[...e];for(let e=0;e<i.length;e+=1)for(let a of t.get(i[e])??[]){if(r.has(a))continue;let e=n.get(a);e!==void 0&&e!==`screen`&&e!==`start`&&(r.add(a),i.push(a))}return r}function h(e,t,n,r){let i=new Map,a=new Map;for(let[t,o]of e)a.set(t,o.kind),i.set(t,p(o.stepKey,n,r));let o=[...e].filter(([e,t])=>t.kind===`screen`&&i.get(e)!==`ahead`).map(([e])=>e);if(o.length===0)return i;let s=new Map,c=new Map;for(let e of t)s.set(e.sourceNodeId,[...s.get(e.sourceNodeId)??[],e.destinationNodeId]),c.set(e.destinationNodeId,[...c.get(e.destinationNodeId)??[],e.sourceNodeId]);let l=[...e].filter(([,e])=>e.kind===`start`).map(([e])=>e),u=m([...o,...l],s,a),d=m(o,c,a);for(let t of e.keys())u.has(t)&&d.has(t)&&i.set(t,`visited`);for(let e of l)i.set(e,`visited`);return i}function g(e,t,n,r){return{id:e,nodeId:t,reference:n,handle:r,parent:null,children:[],index:0,depth:0,prelim:0,mod:0,change:0,shift:0,thread:null,ancestor:null,defaultAncestor:null,x:0}}function _(e){let t=new Map;for(let n of e){let e=t.get(n.sourceNodeId)??[];e.some(e=>e.destinationNodeId===n.destinationNodeId)||(e.push(n),t.set(n.sourceNodeId,e))}let n=e=>e.sourceHandle===void 0?2:l.get(e.sourceHandle)?.order??2;for(let e of t.values())e.sort((e,t)=>n(e)-n(t));return t}function v(e,t,n,r){let i=[],a=[],o=new Set([e.destinationNodeId]),s=[e.destinationNodeId];for(let c=0;c<s.length;c+=1){let l=(t.get(s[c])??[]).filter(e=>r.has(e.destinationNodeId));l.length===0&&a.push({edge:e,id:s[c]});for(let t of l){let r=t.destinationNodeId;o.has(r)||(o.add(r),n(r)?s.push(r):i.push({...e,id:`${e.id}~${r}`,destinationNodeId:r}))}}return{out:i,ended:a}}function y(e,t,n){let r=new Map,i=new Map;for(let[a,o]of e){if(t(a))continue;let s=[],c=[];for(let r of o){if(!n.has(r.destinationNodeId))continue;if(!t(r.destinationNodeId)){s.push(r);continue}let i=v(r,e,t,n);s.push(...i.out),c.push(...i.ended)}r.set(a,s.filter((e,t)=>s.findIndex(t=>t.destinationNodeId===e.destinationNodeId)===t)),c.length>0&&i.set(a,c)}return{through:r,ends:i}}function b(e,t,n){return e.map(({edge:e,id:r})=>{let i=t.kind===`decision`&&e.sourceHandle!==void 0?l.get(e.sourceHandle):void 0;return{id:r,key:`${e.id}~${r}`,label:n.get(r)?.label??r,armLabel:i?.label??null,arm:i?.arm??null}})}function x(e,t,n,r){let i=r;for(let r of t){if(!n.has(r.destinationNodeId))continue;if(i>=c)return{count:i,cut:!0};let t=g(r.destinationNodeId,r.destinationNodeId,!1,r.sourceHandle);t.parent=e,t.index=e.children.length,t.depth=e.depth+1,e.children.push(t),i+=1}return{count:i,cut:!1}}function S(e,t,n){let r=g(e,e,!1,void 0),i=new Set,a=[r],o=1,s=!1;for(let e=a.pop();e!==void 0;e=a.pop()){if(i.has(e.nodeId)){e.reference=!0,e.id=`${e.parent?.id??``}>${e.nodeId}`;continue}i.add(e.nodeId);let r=x(e,t.get(e.nodeId)??[],n,o);o=r.count,s||=r.cut;for(let t=e.children.length-1;t>=0;--t)a.push(e.children[t])}return{root:r,truncated:s}}function C(e,t){return(e.parent===t.parent?1:2)*88}function w(e){return e.children.at(0)??e.thread}function T(e){return e.children.at(-1)??e.thread}function E(e,t,n){let r=n/(t.index-e.index);t.change-=r,t.shift+=n,e.change+=r,t.prelim+=n,t.mod+=n}function D(e){let t=0,n=0;for(let r=e.children.length-1;r>=0;--r){let i=e.children[r];i.prelim+=t,i.mod+=t,n+=i.change,t+=i.shift+n}}function O(e,t,n){let r=e.ancestor??e;return r.parent===t.parent?r:n}function k(e,t,n){if(t===null||e.parent===null)return n;let r=n,i=e,a=e,o=t,s=e.parent.children[0],c=i.mod,l=a.mod,u=o.mod,d=s.mod;for(o=T(o),i=w(i);o!==null&&i!==null;){s=w(s)??s,a=T(a)??a,a.ancestor=e;let t=o.prelim+u-i.prelim-c+C(o,i);t>0&&(E(O(o,e,r),e,t),c+=t,l+=t),u+=o.mod,c+=i.mod,d+=s.mod,l+=a.mod,o=T(o),i=w(i)}return o!==null&&T(a)===null&&(a.thread=o,a.mod+=u-l),i!==null&&w(s)===null&&(s.thread=i,s.mod+=c-d,r=e),r}function A(e,t){let n=e.parent??t,r=e.index>0?n.children[e.index-1]:null;if(e.children.length>0){D(e);let t=(e.children[0].prelim+(e.children.at(-1)?.prelim??0))/2;r===null?e.prelim=t:(e.prelim=r.prelim+C(e,r),e.mod=e.prelim-t)}else r!==null&&(e.prelim=r.prelim+C(e,r));n.defaultAncestor=k(e,r,n.defaultAncestor??n.children[0])}function j(e){let t=[e],n=[];for(let e=t.pop();e!==void 0;e=t.pop())n.push(e),t.push(...e.children);return n.reverse()}function M(e){let t=[e],n=[];for(let e=t.pop();e!==void 0;e=t.pop()){n.push(e);for(let n=e.children.length-1;n>=0;--n)t.push(e.children[n])}return n}function N(e){let t=g(``,``,!1,void 0);t.children=[e];let n=j(e);for(let e of n)A(e,t);e.mod=-e.prelim,e.x=e.prelim+e.mod;for(let e=n.length-1;e>=0;--e){let t=n[e];t.parent!==null&&(t.x=t.prelim+t.parent.mod,t.mod+=t.parent.mod)}return n}function P(e,t,n,r){let i=(e+n)/2;return`M ${e} ${t} C ${i} ${t}, ${i} ${r}, ${n} ${r}`}function F(e,t,n,r){let i=n<=e,a=i?60+(e-n)*.15:Math.max(60,(n-e)/2),o=i?n+a:n-a;return{path:`M ${e} ${t} C ${e+a} ${t}, ${o} ${r}, ${n} ${r}`,back:i}}var I={nodes:[],links:[],jumps:[],width:0,height:0,truncated:!1};function L({nodes:e,edges:t,conditions:n,fieldLookup:r,visited:i,currentStepKey:a}){let c=e.find(e=>e.type===`origin`);if(c===void 0)return I;let d=new Map(n.map(e=>[e.id,e])),p=new Map(e.map(e=>[e.id,f(e,d,r)])),m=h(p,t,i,a),g=e=>p.get(e)?.kind===`system`,{through:v,ends:x}=y(_(t),g,new Set(p.keys())),C=new Set([...p.keys()].filter(e=>!g(e))),{root:w,truncated:T}=S(c.id,v,C),E=N(w),D=1/0,O=-1/0,k=0;for(let e of E)D=Math.min(D,e.x),O=Math.max(O,e.x),k=Math.max(k,e.depth);let A=M(w),j=A.map(e=>{let t=p.get(e.nodeId)??u;return{...t,id:e.id,nodeId:e.nodeId,state:m.get(e.nodeId)??`ahead`,reference:e.reference,leaf:e.children.length===0,ends:e.reference?[]:b(x.get(e.nodeId)??[],t,p),depth:e.depth,x:o+e.depth*220,y:s+(e.x-D)}}),L=new Map(j.map(e=>[e.id,e])),R=A.flatMap(e=>{let t=e.parent===null?void 0:L.get(e.parent.id),n=L.get(e.id);if(t===void 0||n===void 0)return[];let r=t.kind===`decision`&&e.handle!==void 0?l.get(e.handle):void 0;return[{id:n.id,from:t.id,to:n.id,arm:r?.arm??null,label:r?.label??null,path:P(t.x,t.y,n.x,n.y),labelX:(t.x+n.x)/2,labelY:(t.y+n.y)/2-6,travelled:t.state!==`ahead`&&n.state!==`ahead`}]}),z=new Map(j.filter(e=>!e.reference).map(e=>[e.nodeId,e]));return{nodes:j,links:R,jumps:j.flatMap(e=>{let t=e.reference?z.get(e.nodeId):void 0;return t===void 0?[]:[{id:e.id,from:e.id,to:t.id,...F(e.x,e.y,t.x,t.y)}]}),width:220+k*220,height:112+(O-D),truncated:T}}export{l as ARMS,i as BREADTH,a as DEPTH,u as FALLBACK,L as buildTree,f as describeNode,h as graphStates,b as namedEnds,_ as orderedOutgoing,y as withoutActions};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isBlank as e}from"../answer-history/answer-history.js";var t=400;function n(e,t,n){let r=e.edges.filter(e=>e.sourceNodeId===t.id),i=r.find(e=>e.sourceHandle===n),a=r.at(0);return n!==null&&(a=t.type===`condition`?i:i??r.find(e=>e.sourceHandle==null)),e.nodes.find(e=>e.id===a?.destinationNodeId)}function r(e){return e?.nodes.find(e=>e.type===`origin`)}function i(t,n){return t===void 0||e(t)?!1:n===void 0?!0:Array.isArray(t)?t.every(e=>n.includes(e)):n.includes(t)}async function a(e,t,n){let r=[],i=[];for(let[a,o]of e.entries())(o.condition==null||await n.evaluate(o.condition,t))&&(r.push(o.value),i.push(a));return{values:r,positions:i}}async function o(t,n,r){let i={...n};for(let n of t.fieldKeys)Object.hasOwn(r.answers,n)&&!e(r.answers[n])&&(i[n]=r.answers[n]);let o={fields:[],offered:{},options:{}};for(let e of t.fieldKeys){let t=r.field(e),n=t?.condition!=null&&!await r.evaluate(t.condition,i);if(r.codeOwned.has(e)||n||(o.fields.push(e),t?.options==null||t.options.length===0))continue;let{values:s,positions:c}=await a(t.options,i,r);o.offered[e]=s,o.options[e]=c}return o}function s(e,t){let r=n(e.journey,e.node,t);return r===void 0?void 0:{journey:e.journey,node:r}}function c(e,t){let n=e.journeys.get(t),i=r(n);return n===void 0||i===void 0?void 0:{journey:n,node:i}}async function l(e,t,n,r){let a=e.node.stepId===void 0?void 0:n.steps.get(e.node.stepId);if(a===void 0||n.selfAdvancing.has(a.key))return{next:s(e,null)};let{fields:c,offered:l,options:u}=await o(a,r.held,n),d=c.every(e=>i(n.answers[e],l[e]));r.rows.push({kind:`screen`,id:t,experienceKey:e.journey.key,experienceName:e.journey.name,stepKey:a.key,stepName:a.name??null,fields:c,options:u,complete:d});for(let e of c)i(n.answers[e],l[e])&&(r.held[e]=n.answers[e]);let f={experienceKey:e.journey.key,experienceName:e.journey.name,stepKey:a.key,stepName:a.name??null};return r.lastScreen=f,d?(r.passed.push({experienceKey:e.journey.key,stepKey:a.key}),{next:s(e,null)}):{stop:f}}async function u(e,t,n,r){let i=e.node.conditionId===void 0?void 0:n.conditions.get(e.node.conditionId);if(i===void 0)return r.rows.push({kind:`end`,id:t,reason:`end`,name:e.node.name??null}),{stop:null};let a=await n.evaluate(i.logic,r.held);if(n.readsOf(i.logic).some(e=>!n.asked.has(e)||n.codeOwned.has(e))){a=n.choices[t]??a;let e=i.name??i.key??`
|
|
1
|
+
import{isBlank as e}from"../answer-history/answer-history.js";var t=400;function n(e,t,n){let r=e.edges.filter(e=>e.sourceNodeId===t.id),i=r.find(e=>e.sourceHandle===n),a=r.at(0);return n!==null&&(a=t.type===`condition`?i:i??r.find(e=>e.sourceHandle==null)),e.nodes.find(e=>e.id===a?.destinationNodeId)}function r(e){return e?.nodes.find(e=>e.type===`origin`)}function i(t,n){return t===void 0||e(t)?!1:n===void 0?!0:Array.isArray(t)?t.every(e=>n.includes(e)):n.includes(t)}async function a(e,t,n){let r=[],i=[];for(let[a,o]of e.entries())(o.condition==null||await n.evaluate(o.condition,t))&&(r.push(o.value),i.push(a));return{values:r,positions:i}}async function o(t,n,r){let i={...n};for(let n of t.fieldKeys)Object.hasOwn(r.answers,n)&&!e(r.answers[n])&&(i[n]=r.answers[n]);let o={fields:[],offered:{},options:{}};for(let e of t.fieldKeys){let t=r.field(e),n=t?.condition!=null&&!await r.evaluate(t.condition,i);if(r.codeOwned.has(e)||n||(o.fields.push(e),t?.options==null||t.options.length===0))continue;let{values:s,positions:c}=await a(t.options,i,r);o.offered[e]=s,o.options[e]=c}return o}function s(e,t){let r=n(e.journey,e.node,t);return r===void 0?void 0:{journey:e.journey,node:r}}function c(e,t){let n=e.journeys.get(t),i=r(n);return n===void 0||i===void 0?void 0:{journey:n,node:i}}async function l(e,t,n,r){let a=e.node.stepId===void 0?void 0:n.steps.get(e.node.stepId);if(a===void 0||n.selfAdvancing.has(a.key))return{next:s(e,null)};let{fields:c,offered:l,options:u}=await o(a,r.held,n),d=c.every(e=>i(n.answers[e],l[e]));r.rows.push({kind:`screen`,id:t,experienceKey:e.journey.key,experienceName:e.journey.name,stepKey:a.key,stepName:a.name??null,fields:c,options:u,complete:d});for(let e of c)i(n.answers[e],l[e])&&(r.held[e]=n.answers[e]);let f={experienceKey:e.journey.key,experienceName:e.journey.name,stepKey:a.key,stepName:a.name??null};return r.lastScreen=f,d?(r.passed.push({experienceKey:e.journey.key,stepKey:a.key}),{next:s(e,null)}):{stop:f}}async function u(e,t,n,r){let i=e.node.conditionId===void 0?void 0:n.conditions.get(e.node.conditionId);if(i===void 0)return r.rows.push({kind:`end`,id:t,reason:`end`,name:e.node.name??null}),{stop:null};let a=await n.evaluate(i.logic,r.held);if(n.readsOf(i.logic).some(e=>!n.asked.has(e)||n.codeOwned.has(e))){a=n.choices[t]??a;let e=i.name??i.key??`An unnamed condition`;r.rows.push({kind:`choice`,id:t,question:e,value:a})}return{next:s(e,a?`true`:`false`)}}function d(e,t,n,r){let{node:i}=e,a=i.type===`experience`?i.experienceKey:n.handoffs[i.actionKey??``];if(a!==void 0){let e=c(n,a);return e===void 0?(r.rows.push({kind:`end`,id:t,reason:`handoff`,name:i.name??a}),{stop:null}):{next:e}}if(i.type!==`action`)return{next:s(e,null)};let o=e.journey.edges.some(e=>e.sourceNodeId===i.id&&e.sourceHandle===`false`),l=!0;if(o){l=n.choices[t]??!0;let e=`${i.name??i.actionKey??`This action`} works`;r.rows.push({kind:`choice`,id:t,question:e,value:l})}return{next:s(e,l?`true`:`false`)}}async function f(e,t,n,r){switch(e.node.type){case`step`:return l(e,t,n,r);case`condition`:return u(e,t,n,r);case`action`:case`experience`:return d(e,t,n,r);default:return{next:s(e,null)}}}async function p(e){let n={held:{},rows:[],passed:[],lastScreen:null},r=e=>({rows:n.rows,answers:{...n.held},passed:n.passed,landing:e??n.lastScreen}),i=new Set,a=c(e,e.start);for(let o=0;o<t;o++){if(a===void 0)return n.rows.push({kind:`end`,id:`end-${o}`,reason:`end`,name:null}),r(null);let t=`${a.journey.key}/${a.node.id}`;if(i.has(t)){let i=a.node.stepId,o=i===void 0?void 0:e.steps.get(i);return n.rows.push({kind:`end`,id:`${t}#loop`,reason:`loop`,name:o?.name??null}),r(null)}i.add(t);let s=await f(a,t,e,n);if(`stop`in s)return r(s.stop);a=s.next}return n.rows.push({kind:`end`,id:`limit`,reason:`limit`,name:null}),r(null)}function m(e){let t=Array.isArray(e)?e.at(0):e;if(typeof t!=`string`||!t.startsWith(`responses.`))return;let n=t.slice(10).split(`.`).at(0);return n===void 0||n.length===0?void 0:n}function h(e){let t=new Set,n=e=>{if(Array.isArray(e))for(let t of e)n(t);else if(typeof e==`object`&&e)for(let[r,i]of Object.entries(e)){let e=r===`var`?m(i):void 0;e!==void 0&&t.add(e),n(i)}};return n(e),[...t]}function g(e,t){let n=e.filter(e=>!t.has(e.key)).map(e=>({experience:e,tree:e.treeStore.getState()}));return{journeys:new Map(n.map(({experience:e,tree:t})=>[e.key,{key:e.key,name:e.name,nodes:t.nodes,edges:t.edges}])),steps:new Map(n.flatMap(({tree:e})=>e.steps.map(e=>[e.id,e]))),conditions:new Map(n.flatMap(({tree:e})=>e.conditions.map(e=>[e.id,e])))}}export{g as builderGraph,h as responseKeysOf,p as walkDraft};
|
|
@@ -199,6 +199,6 @@ export declare function sitePersonas(definitions: unknown): Persona[];
|
|
|
199
199
|
*
|
|
200
200
|
* @param {PersonaLanding | null} landing - The persona's landing.
|
|
201
201
|
* @returns {string} A short phrase naming the screen and journey.
|
|
202
|
-
* @example describeLanding(null) // '
|
|
202
|
+
* @example describeLanding(null) // 'Responses only'
|
|
203
203
|
*/
|
|
204
204
|
export declare function describeLanding(landing: PersonaLanding | null): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isBlank as e}from"../answer-history/answer-history.js";import{deslug as t}from"../plain-language/plain-language.js";var n=`picked-devtools-personas`,r=1;function i(e){return`${n}:${e}`}function a(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function o(e){return e===null||typeof e==`string`||typeof e==`boolean`?!0:typeof e==`number`?Number.isFinite(e):Array.isArray(e)?e.every(o):a(e)&&Object.values(e).every(o)}function s(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))o(r)&&(t[n]=r);return t}function c(e){if(typeof e!=`string`)return null;let t=e.trim();return t.length>0?t:null}function l(e){if(!a(e))return null;let t=c(e.experienceKey);return t===null?null:{experienceKey:t,experienceName:c(e.experienceName),stepKey:c(e.stepKey),stepName:c(e.stepName)}}function u(e){return Array.isArray(e)?e.flatMap(e=>{if(!a(e))return[];let t=c(e.experienceKey),n=c(e.stepKey);return t===null||n===null?[]:[{experienceKey:t,stepKey:n}]}):[]}function d(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r==`boolean`&&(t[n]=r);return t}function f(e){let t=c(e);return t!==null&&t.startsWith(`/`)&&!t.startsWith(`//`)?t:null}function p(e,t){let n=e.trim();if(n.length===0)return null;try{let e=new URL(n,t);return e.origin===t?`${e.pathname}${e.search}`:null}catch{return null}}function m(e){if(!a(e))return null;let t=c(e.id),n=c(e.name);return t===null||n===null?null:{id:t,name:n,description:c(e.description),source:`saved`,responses:s(e.responses),landing:l(e.landing),passed:u(e.passed),choices:d(e.choices),entryUrl:f(e.entryUrl),savedAt:c(e.savedAt),load:null}}function h(e,t){let n;try{n=e().getItem(i(t))}catch{return[]}if(n===null)return[];let r;try{r=JSON.parse(n)}catch{return[]}return!a(r)||!Array.isArray(r.personas)?[]:r.personas.map(m).filter(e=>e!==null)}function g(e,t,n){let a=n.map(e=>({id:e.id,name:e.name,description:e.description,responses:e.responses,landing:e.landing,passed:e.passed,choices:e.choices,entryUrl:e.entryUrl,savedAt:e.savedAt}));try{return e().setItem(i(t),JSON.stringify({version:r,personas:a})),!0}catch{return!1}}function _(e,t){let n=new Set,r=[];for(let{journey:i,screen:a}of e){let e=`${i}/${a}`;t.has(i)||n.has(e)||(n.add(e),r.push({experienceKey:i,stepKey:a}))}return r}function v(t,n){let r={};for(let[i,a]of Object.entries(t))n.has(i)&&!e(a)&&(r[i]=a);return r}function y(e,t){let n=c(e.name);return n===null?{ok:!1,error:`Give it a name.`}:Object.keys(t.responses).length===0&&t.journey===null?{ok:!1,error:`Nothing to save yet.
|
|
1
|
+
import{isBlank as e}from"../answer-history/answer-history.js";import{deslug as t}from"../plain-language/plain-language.js";var n=`picked-devtools-personas`,r=1;function i(e){return`${n}:${e}`}function a(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function o(e){return e===null||typeof e==`string`||typeof e==`boolean`?!0:typeof e==`number`?Number.isFinite(e):Array.isArray(e)?e.every(o):a(e)&&Object.values(e).every(o)}function s(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))o(r)&&(t[n]=r);return t}function c(e){if(typeof e!=`string`)return null;let t=e.trim();return t.length>0?t:null}function l(e){if(!a(e))return null;let t=c(e.experienceKey);return t===null?null:{experienceKey:t,experienceName:c(e.experienceName),stepKey:c(e.stepKey),stepName:c(e.stepName)}}function u(e){return Array.isArray(e)?e.flatMap(e=>{if(!a(e))return[];let t=c(e.experienceKey),n=c(e.stepKey);return t===null||n===null?[]:[{experienceKey:t,stepKey:n}]}):[]}function d(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r==`boolean`&&(t[n]=r);return t}function f(e){let t=c(e);return t!==null&&t.startsWith(`/`)&&!t.startsWith(`//`)?t:null}function p(e,t){let n=e.trim();if(n.length===0)return null;try{let e=new URL(n,t);return e.origin===t?`${e.pathname}${e.search}`:null}catch{return null}}function m(e){if(!a(e))return null;let t=c(e.id),n=c(e.name);return t===null||n===null?null:{id:t,name:n,description:c(e.description),source:`saved`,responses:s(e.responses),landing:l(e.landing),passed:u(e.passed),choices:d(e.choices),entryUrl:f(e.entryUrl),savedAt:c(e.savedAt),load:null}}function h(e,t){let n;try{n=e().getItem(i(t))}catch{return[]}if(n===null)return[];let r;try{r=JSON.parse(n)}catch{return[]}return!a(r)||!Array.isArray(r.personas)?[]:r.personas.map(m).filter(e=>e!==null)}function g(e,t,n){let a=n.map(e=>({id:e.id,name:e.name,description:e.description,responses:e.responses,landing:e.landing,passed:e.passed,choices:e.choices,entryUrl:e.entryUrl,savedAt:e.savedAt}));try{return e().setItem(i(t),JSON.stringify({version:r,personas:a})),!0}catch{return!1}}function _(e,t){let n=new Set,r=[];for(let{journey:i,screen:a}of e){let e=`${i}/${a}`;t.has(i)||n.has(e)||(n.add(e),r.push({experienceKey:i,stepKey:a}))}return r}function v(t,n){let r={};for(let[i,a]of Object.entries(t))n.has(i)&&!e(a)&&(r[i]=a);return r}function y(e,t){let n=c(e.name);return n===null?{ok:!1,error:`Give it a name.`}:Object.keys(t.responses).length===0&&t.journey===null?{ok:!1,error:`Nothing to save yet. Give a response first.`}:{ok:!0,persona:{id:t.id,name:n,description:c(e.description),source:`saved`,responses:s(t.responses),landing:t.journey,passed:t.passed,choices:d(t.choices),entryUrl:f(t.entryUrl),savedAt:t.savedAt,load:null}}}function b(e){return Array.isArray(e)?e.flatMap(e=>{if(!a(e))return[];let t=c(e.id),n=c(e.name);if(t===null||n===null)return[];let r=c(e.experienceKey),i=e.load;return[{id:t,name:n,description:c(e.description),source:`site`,responses:s(e.responses),landing:r===null?null:{experienceKey:r,experienceName:null,stepKey:c(e.stepKey),stepName:null},passed:[],choices:{},entryUrl:f(e.entryUrl),savedAt:null,load:typeof i==`function`?async e=>{await Reflect.apply(i,void 0,[e])}:null}]}):[]}function x(e){if(e===null)return`Responses only`;let n=e.experienceName??t(e.experienceKey);return e.stepKey===null?`Opens ${n}`:`Lands on ${e.stepName??t(e.stepKey)} in ${n}`}export{n as PERSONA_STORAGE_PREFIX,y as buildPersona,v as customerAnswers,x as describeLanding,p as entryUrlOf,_ as passedScreens,i as personaStorageKey,s as readAnswers,f as readEntryUrl,h as readSavedPersonas,b as sitePersonas,g as writeSavedPersonas};
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file The
|
|
2
|
+
* @file The panel's vocabulary contract: every label and intro the review tabs share.
|
|
3
3
|
*
|
|
4
|
-
* One module owns
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Picked dashboard does not belong on a reviewer's screen.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Each node type, named the way a reviewer would describe what the journey is doing there.
|
|
4
|
+
* One module owns these words, so they are reviewable in one place and cannot drift into a
|
|
5
|
+
* component as a hard-coded string. The panel speaks the dashboard's nouns (experience, step,
|
|
6
|
+
* field, response, condition, action, feature flag, visitor), so what a person reads in the panel
|
|
7
|
+
* is what they find in the dashboard.
|
|
12
8
|
*/
|
|
9
|
+
/** Each node type's label, in the dashboard's nouns. */
|
|
13
10
|
export declare const NODE_LABELS: {
|
|
14
|
-
readonly origin: '
|
|
15
|
-
readonly step: '
|
|
16
|
-
readonly condition: '
|
|
17
|
-
readonly action: '
|
|
18
|
-
readonly experience: 'Opens another
|
|
11
|
+
readonly origin: 'Origin';
|
|
12
|
+
readonly step: 'Step';
|
|
13
|
+
readonly condition: 'Condition';
|
|
14
|
+
readonly action: 'Action';
|
|
15
|
+
readonly experience: 'Opens another experience';
|
|
19
16
|
};
|
|
20
17
|
/**
|
|
21
18
|
* Turns a kebab or snake key into a sentence. It is a fallback for a step whose author gave it no
|
|
@@ -33,7 +30,7 @@ export declare function deslug(key: string): string;
|
|
|
33
30
|
*
|
|
34
31
|
* @param {string | undefined} name - The step's authored name.
|
|
35
32
|
* @param {string | undefined} key - The step's key.
|
|
36
|
-
* @returns {string} A non-empty label for the
|
|
33
|
+
* @returns {string} A non-empty label for the step.
|
|
37
34
|
* @example
|
|
38
35
|
* screenName(undefined, 'plan-comparison') // 'Plan comparison'
|
|
39
36
|
*/
|
|
@@ -55,10 +52,10 @@ export declare function configSourceLabel(previewToken: string | undefined): str
|
|
|
55
52
|
export declare const TAB_LABELS: {
|
|
56
53
|
readonly guide: 'How to use';
|
|
57
54
|
readonly session: 'This session';
|
|
58
|
-
readonly answers: '
|
|
59
|
-
readonly journey: '
|
|
55
|
+
readonly answers: 'Responses';
|
|
56
|
+
readonly journey: 'Flow';
|
|
60
57
|
readonly personas: 'Personas & Scenarios';
|
|
61
|
-
readonly variations: '
|
|
58
|
+
readonly variations: 'Feature flags';
|
|
62
59
|
};
|
|
63
60
|
/** The id of one of the review tools' own tabs. */
|
|
64
61
|
export type ReviewTabId = keyof typeof TAB_LABELS;
|
|
@@ -67,9 +64,9 @@ export declare const DEVELOPER_LABEL = "Developer";
|
|
|
67
64
|
/** The one-sentence purpose each tab leads with. Kept together so the tone stays consistent. */
|
|
68
65
|
export declare const TAB_INTROS: {
|
|
69
66
|
readonly guide: 'How to use each tab. Open a tab straight from its section.';
|
|
70
|
-
readonly session: 'Where you are right now, and
|
|
71
|
-
readonly answers: 'Every
|
|
72
|
-
readonly journey: 'Every route this
|
|
73
|
-
readonly personas: '
|
|
74
|
-
readonly variations: 'The
|
|
67
|
+
readonly session: 'Where you are right now, and your responses on each step. Start here if something looks wrong.';
|
|
68
|
+
readonly answers: 'Every field this experience can ask for, and the responses given. Change one to see what the visitor would see.';
|
|
69
|
+
readonly journey: 'Every route this experience can take, with your route marked. Go back to any step you have already visited.';
|
|
70
|
+
readonly personas: 'Visitors and situations to try this experience as. Load one to land where it would, or save where you are as a new one.';
|
|
71
|
+
readonly variations: 'The feature flags running on this site. Force a variant to review it, then copy the link to show someone else exactly what you saw.';
|
|
75
72
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={origin:`
|
|
1
|
+
var e={origin:`Origin`,step:`Step`,condition:`Condition`,action:`Action`,experience:`Opens another experience`};function t(e){let t=e.replace(/[-_]+/g,` `).trim();return t.length===0?e:t.charAt(0).toUpperCase()+t.slice(1)}function n(e,n){let r=e?.trim();return r!==void 0&&r.length>0?r:n!==void 0&&n.length>0?t(n):`Untitled step`}function r(e){return e!==void 0&&e.length>0?`Viewing unpublished changes`:`Viewing published content`}var i={guide:`How to use`,session:`This session`,answers:`Responses`,journey:`Flow`,personas:`Personas & Scenarios`,variations:`Feature flags`},a=`Developer`,o={guide:`How to use each tab. Open a tab straight from its section.`,session:`Where you are right now, and your responses on each step. Start here if something looks wrong.`,answers:`Every field this experience can ask for, and the responses given. Change one to see what the visitor would see.`,journey:`Every route this experience can take, with your route marked. Go back to any step you have already visited.`,personas:`Visitors and situations to try this experience as. Load one to land where it would, or save where you are as a new one.`,variations:`The feature flags running on this site. Force a variant to review it, then copy the link to show someone else exactly what you saw.`};export{a as DEVELOPER_LABEL,e as NODE_LABELS,o as TAB_INTROS,i as TAB_LABELS,r as configSourceLabel,t as deslug,n as screenName};
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
export interface FieldSummary {
|
|
16
16
|
/** The short noun ("Purpose"). What a sentence is built from. */
|
|
17
17
|
name?: string;
|
|
18
|
-
/** The
|
|
18
|
+
/** The label the visitor reads, which the Responses tab shows. */
|
|
19
19
|
label?: string;
|
|
20
20
|
options?: readonly {
|
|
21
21
|
value: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=`a rule this panel can't put into words`,t={"==":`is`,"===":`is`,"!=":`is not`,"!==":`is not`,">":`is more than`,">=":`is at least`,"<":`is less than`,"<=":`is at most`},n=`responses.`;function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e){if(!r(e))return;let t=Object.keys(e);return t.length===1?t[0]:void 0}function a(e){if(typeof e==`string`)return e;if(Array.isArray(e)&&typeof e[0]==`string`)return e[0]}function o(e){if(!r(e)||i(e)!==`var`)return;let t=a(e.var);if(t!==void 0)return t.startsWith(n)?t.slice(10):t}function s(e,t){let n=t(e)?.name?.trim();return n!==void 0&&n.length>0?n:e.replace(/[-_]+/g,` `)}function c(t,n,r){if(n!==void 0){let e=r(n)?.options?.find(e=>e.value===t);if(e!==void 0)return e.label}return t===null?`nothing`:t===!0?`yes`:t===!1?`no`:typeof t==`string`?t.length===0?`nothing`:t:typeof t==`number`?String(t):e}function l(e,t){return e.length<=1?e.join(``):e.length===2?`${e[0]} ${t} ${e[1]}`:`${e.slice(0,-1).join(`, `)} ${t} ${e[e.length-1]}`}function u(n,r,i){let[a,l]=r,u=o(a);return u===void 0?e:`${s(u,i)} ${t[n]} ${c(l,u,i)}`}function d(t,n){let[r,i]=t,a=o(r);if(a===void 0||!Array.isArray(i))return e;let u=i.map(e=>c(e,a,n));return`${s(a,n)} is ${l(u,`or`)}`}function f(e){return e===!0?`Always`:e===!1?`Never`:e==null?`Always`:null}function p(e){return e.includes(` is not `)?e.replace(` is not `,` is `):e.includes(` is `)?e.replace(` is `,` is not `):e.endsWith(` has
|
|
1
|
+
var e=`a rule this panel can't put into words`,t={"==":`is`,"===":`is`,"!=":`is not`,"!==":`is not`,">":`is more than`,">=":`is at least`,"<":`is less than`,"<=":`is at most`},n=`responses.`;function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e){if(!r(e))return;let t=Object.keys(e);return t.length===1?t[0]:void 0}function a(e){if(typeof e==`string`)return e;if(Array.isArray(e)&&typeof e[0]==`string`)return e[0]}function o(e){if(!r(e)||i(e)!==`var`)return;let t=a(e.var);if(t!==void 0)return t.startsWith(n)?t.slice(10):t}function s(e,t){let n=t(e)?.name?.trim();return n!==void 0&&n.length>0?n:e.replace(/[-_]+/g,` `)}function c(t,n,r){if(n!==void 0){let e=r(n)?.options?.find(e=>e.value===t);if(e!==void 0)return e.label}return t===null?`nothing`:t===!0?`yes`:t===!1?`no`:typeof t==`string`?t.length===0?`nothing`:t:typeof t==`number`?String(t):e}function l(e,t){return e.length<=1?e.join(``):e.length===2?`${e[0]} ${t} ${e[1]}`:`${e.slice(0,-1).join(`, `)} ${t} ${e[e.length-1]}`}function u(n,r,i){let[a,l]=r,u=o(a);return u===void 0?e:`${s(u,i)} ${t[n]} ${c(l,u,i)}`}function d(t,n){let[r,i]=t,a=o(r);if(a===void 0||!Array.isArray(i))return e;let u=i.map(e=>c(e,a,n));return`${s(a,n)} is ${l(u,`or`)}`}function f(e){return e===!0?`Always`:e===!1?`Never`:e==null?`Always`:null}function p(e){return e.includes(` is not `)?e.replace(` is not `,` is `):e.includes(` is `)?e.replace(` is `,` is not `):e.endsWith(` has a response`)?`${e.slice(0,-15)} has no response`:`not: ${e}`}function m(n,r,i){if(n in t)return r.length>=2?u(n,r,i):e;if(n===`and`||n===`or`)return r.length===0?e:l(r.map(e=>h(e,i)),n);if(n===`!`||n===`not`){let t=h(r[0],i);return t===`a rule this panel can't put into words`?e:p(t)}if(n===`!!`){let t=o(r[0]);return t===void 0?e:`${s(t,i)} has a response`}return n===`in`?d(r,i):e}function h(t,n){let a=f(t);if(a!==null)return a;let c=o(t);if(c!==void 0)return`${s(c,n)} has a response`;let l=i(t);if(l===void 0||!r(t))return e;let u=t[l];return m(l,Array.isArray(u)?u:[u],n)}export{e as UNDESCRIBABLE,h as describeRule,o as varKeyOf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return e.readOnly?{readOnly:!0,canEditAnswers:!1,canNavigate:!1,reason:`You're on the live site — this is read-only so a real
|
|
1
|
+
function e(e){return e.readOnly?{readOnly:!0,canEditAnswers:!1,canNavigate:!1,reason:`You're on the live site — this is read-only so a real visitor's session can't be changed.`,label:e.environmentLabel??`Live site`}:{readOnly:!1,canEditAnswers:!0,canNavigate:!0,reason:null,label:e.environmentLabel??null}}export{e as derivePosture};
|
|
@@ -20,26 +20,27 @@ export interface LauncherOptions {
|
|
|
20
20
|
*/
|
|
21
21
|
autoHide?: boolean;
|
|
22
22
|
}
|
|
23
|
-
/** How loading a persona walks
|
|
23
|
+
/** How loading a persona walks an experience. */
|
|
24
24
|
export interface PersonaWalkOptions {
|
|
25
25
|
/**
|
|
26
26
|
* Step keys that move on by themselves, such as a loader that continues once its fetch lands.
|
|
27
|
-
* The walk waits on them, where it presses Continue on any other
|
|
27
|
+
* The walk waits on them, where it presses Continue on any other step.
|
|
28
28
|
*/
|
|
29
29
|
selfAdvancingSteps?: readonly string[];
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
* on a resume prompt.
|
|
31
|
+
* Responses every walk gives when a step asks for their fields, over the persona's own, such as
|
|
32
|
+
* "start over" on a resume prompt.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
responses?: Readonly<Record<string, FieldValue>>;
|
|
35
35
|
/**
|
|
36
|
-
* Actions that open another
|
|
37
|
-
* persona in the panel carries on in that
|
|
36
|
+
* Actions that open another experience, by action key, with that experience's key. Building a
|
|
37
|
+
* persona in the panel carries on in that experience where the action is; a launch node needs
|
|
38
|
+
* none.
|
|
38
39
|
*/
|
|
39
40
|
handoffs?: Readonly<Record<string, string>>;
|
|
40
41
|
}
|
|
41
|
-
/** A button the
|
|
42
|
-
export interface
|
|
42
|
+
/** A button the Responses tab shows after a response is edited. */
|
|
43
|
+
export interface ResponseFollowUp {
|
|
43
44
|
/** The button's text. */
|
|
44
45
|
label: string;
|
|
45
46
|
/** Runs when the button is pressed. */
|
|
@@ -71,7 +72,7 @@ export interface DevtoolsGuide {
|
|
|
71
72
|
/** The options read by the devtools panel, as passed to `svelteDevtoolsPlugin`. */
|
|
72
73
|
export interface DevtoolsPanelOptions {
|
|
73
74
|
/**
|
|
74
|
-
* When `true`, the panel withholds every write —
|
|
75
|
+
* When `true`, the panel withholds every write — response edits, clears and resets, step jumps —
|
|
75
76
|
* and says why. For a session that may belong to a real visitor, such as a preview link opened
|
|
76
77
|
* on a live site. A preview token alone never makes a session read-only.
|
|
77
78
|
*
|
|
@@ -80,27 +81,27 @@ export interface DevtoolsPanelOptions {
|
|
|
80
81
|
readOnly?: boolean;
|
|
81
82
|
/** The environment's name, shown as a badge in the panel header. */
|
|
82
83
|
environmentLabel?: string;
|
|
83
|
-
/** Experience keys the
|
|
84
|
+
/** Experience keys the panel never lists or follows, such as a host's own internal widgets. */
|
|
84
85
|
hiddenExperienceKeys?: readonly string[];
|
|
85
86
|
/**
|
|
86
87
|
* Experience keys, topmost first. When several experiences are open, the first one listed here
|
|
87
|
-
* is the
|
|
88
|
+
* is the experience the panel follows. Unlisted ones follow the most recently opened.
|
|
88
89
|
*/
|
|
89
|
-
|
|
90
|
-
/** Field keys
|
|
90
|
+
experiencePrecedence?: readonly string[];
|
|
91
|
+
/** Field keys that code commits and no visitor fills in; Responses leaves them out. */
|
|
91
92
|
codeOwnedFields?: readonly string[];
|
|
92
|
-
/** A button the
|
|
93
|
-
|
|
93
|
+
/** A button the Responses tab offers after an edit, such as one that re-runs a fetch. */
|
|
94
|
+
responseFollowUp?: ResponseFollowUp;
|
|
94
95
|
/** How to use content for host features that are not devtools tabs. */
|
|
95
96
|
guide?: DevtoolsGuide;
|
|
96
97
|
/** The floating button's corner and auto-hide. */
|
|
97
98
|
launcher?: LauncherOptions;
|
|
98
99
|
/**
|
|
99
|
-
*
|
|
100
|
+
* Visitors and situations every viewer can load from the Personas & Scenarios tab. Reviewers
|
|
100
101
|
* also save their own there, in their browser.
|
|
101
102
|
*/
|
|
102
103
|
personas?: readonly PersonaDefinition[];
|
|
103
|
-
/** How loading a persona walks
|
|
104
|
+
/** How loading a persona walks an experience. */
|
|
104
105
|
personaWalk?: PersonaWalkOptions;
|
|
105
106
|
}
|
|
106
107
|
/** The devtools options with every default applied. */
|
|
@@ -109,9 +110,9 @@ export interface ResolvedDevtoolsOptions {
|
|
|
109
110
|
environmentLabel: string | undefined;
|
|
110
111
|
/** Always includes the devtools experience itself. */
|
|
111
112
|
hiddenExperienceKeys: ReadonlySet<string>;
|
|
112
|
-
|
|
113
|
+
experiencePrecedence: readonly string[];
|
|
113
114
|
codeOwnedFields: ReadonlySet<string>;
|
|
114
|
-
|
|
115
|
+
responseFollowUp: ResponseFollowUp | null;
|
|
115
116
|
guide: {
|
|
116
117
|
sections: Required<DevtoolsGuideSection>[];
|
|
117
118
|
fixes: DevtoolsGuideFix[];
|
|
@@ -120,7 +121,7 @@ export interface ResolvedDevtoolsOptions {
|
|
|
120
121
|
personas: Persona[];
|
|
121
122
|
personaWalk: {
|
|
122
123
|
selfAdvancingSteps: ReadonlySet<string>;
|
|
123
|
-
|
|
124
|
+
responses: Readonly<Record<string, FieldValue>>;
|
|
124
125
|
handoffs: Readonly<Record<string, string>>;
|
|
125
126
|
};
|
|
126
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEVTOOLS_EXPERIENCE_KEY as e,DEVTOOLS_PLUGIN_NAME as t}from"./constants.js";import{readAnswers as n,sitePersonas as r}from"./derive/personas/personas.js";var i=[`top-left`,`top-right`,`bottom-left`,`bottom-right`];function a(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function o(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function s(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r==`string`&&(t[n]=r);return t}function c(e){return i.some(t=>t===e)}function l(e){if(!a(e))return null;let{id:t,title:n,steps:r,notes:i}=e;return typeof t!=`string`||typeof n!=`string`||!Array.isArray(r)?null:{id:t,title:n,steps:o(r),notes:o(i)}}function u(e){if(!a(e))return null;let{problem:t,fix:n}=e;return typeof t!=`string`||typeof n!=`string`?null:{problem:t,fix:n}}function d(e){if(!a(e))return null;let{label:t,run:n}=e;return typeof t!=`string`||typeof n!=`function`?null:{label:t,run:e=>{Reflect.apply(n,void 0,[e])}}}function f(i){let f=i.getPluginConfig(t).options,p=a(f)?f:{},m=a(p.guide)?p.guide:{},h=a(p.launcher)?p.launcher:{},g=a(p.personaWalk)?p.personaWalk:{},_=p.environmentLabel;return{readOnly:p.readOnly===!0,environmentLabel:typeof _==`string`&&_.length>0?_:void 0,hiddenExperienceKeys:new Set([e,...o(p.hiddenExperienceKeys)]),
|
|
1
|
+
import{DEVTOOLS_EXPERIENCE_KEY as e,DEVTOOLS_PLUGIN_NAME as t}from"./constants.js";import{readAnswers as n,sitePersonas as r}from"./derive/personas/personas.js";var i=[`top-left`,`top-right`,`bottom-left`,`bottom-right`];function a(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function o(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`):[]}function s(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r==`string`&&(t[n]=r);return t}function c(e){return i.some(t=>t===e)}function l(e){if(!a(e))return null;let{id:t,title:n,steps:r,notes:i}=e;return typeof t!=`string`||typeof n!=`string`||!Array.isArray(r)?null:{id:t,title:n,steps:o(r),notes:o(i)}}function u(e){if(!a(e))return null;let{problem:t,fix:n}=e;return typeof t!=`string`||typeof n!=`string`?null:{problem:t,fix:n}}function d(e){if(!a(e))return null;let{label:t,run:n}=e;return typeof t!=`string`||typeof n!=`function`?null:{label:t,run:e=>{Reflect.apply(n,void 0,[e])}}}function f(i){let f=i.getPluginConfig(t).options,p=a(f)?f:{},m=a(p.guide)?p.guide:{},h=a(p.launcher)?p.launcher:{},g=a(p.personaWalk)?p.personaWalk:{},_=p.environmentLabel;return{readOnly:p.readOnly===!0,environmentLabel:typeof _==`string`&&_.length>0?_:void 0,hiddenExperienceKeys:new Set([e,...o(p.hiddenExperienceKeys)]),experiencePrecedence:o(p.experiencePrecedence),codeOwnedFields:new Set(o(p.codeOwnedFields)),responseFollowUp:d(p.responseFollowUp),guide:{sections:(Array.isArray(m.sections)?m.sections:[]).map(l).filter(e=>e!==null),fixes:(Array.isArray(m.fixes)?m.fixes:[]).map(u).filter(e=>e!==null)},launcher:{position:c(h.position)?h.position:`bottom-right`,autoHide:typeof h.autoHide!=`boolean`||h.autoHide},personas:r(p.personas),personaWalk:{selfAdvancingSteps:new Set(o(g.selfAdvancingSteps)),responses:n(g.responses),handoffs:s(g.handoffs)}}}export{e as DEVTOOLS_EXPERIENCE_KEY,i as LAUNCHER_POSITIONS,f as readDevtoolsOptions};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* @file The
|
|
4
|
-
* group's Responses screen
|
|
3
|
+
* @file The Responses tab: every field an experience can ask for, and the response given — the
|
|
4
|
+
* Developer group's Responses screen, laid out for a reviewer.
|
|
5
5
|
*
|
|
6
6
|
* Four differences from that screen, all deliberate:
|
|
7
7
|
* 1. Rows are titled by the QUESTION the visitor saw (`field.label`), not `field.key`.
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
)
|
|
77
77
|
|
|
78
78
|
const filters = $derived<{ value: Filter; label: string }[]>([
|
|
79
|
-
{ value: 'answered', label: `
|
|
80
|
-
{ value: 'unanswered', label: `
|
|
79
|
+
{ value: 'answered', label: `With a response (${answeredCount})` },
|
|
80
|
+
{ value: 'unanswered', label: `No response (${rows.length - answeredCount})` },
|
|
81
81
|
{ value: 'all', label: 'All' },
|
|
82
82
|
])
|
|
83
83
|
|
|
@@ -254,15 +254,15 @@
|
|
|
254
254
|
</div>
|
|
255
255
|
|
|
256
256
|
{#if confirmingReset}
|
|
257
|
-
<Banner status="warning" title="Clear every
|
|
257
|
+
<Banner status="warning" title="Clear every response?">
|
|
258
258
|
<Stack direction="vertical" gap="small">
|
|
259
259
|
<Text size="small">
|
|
260
|
-
This empties every
|
|
260
|
+
This empties every response in this session. Nothing about a real visitor changes.
|
|
261
261
|
</Text>
|
|
262
262
|
<Stack direction="horizontal" gap="small">
|
|
263
263
|
<Button variant="destructive" size="small" onclick={reset}>Clear everything</Button>
|
|
264
264
|
<Button variant="secondary" size="small" onclick={() => (confirmingReset = false)}>
|
|
265
|
-
Keep my
|
|
265
|
+
Keep my responses
|
|
266
266
|
</Button>
|
|
267
267
|
</Stack>
|
|
268
268
|
</Stack>
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
again. Saying so beats a reviewer reporting the stale screen as a bug. -->
|
|
275
275
|
<Banner status="info" testId="devtools-answers-follow-up">
|
|
276
276
|
<Stack direction="horizontal" gap="small" justify="center" align="space-between">
|
|
277
|
-
<Text size="small">What
|
|
277
|
+
<Text size="small">What the page shows was worked out before this change.</Text>
|
|
278
278
|
<Button
|
|
279
279
|
variant="secondary"
|
|
280
280
|
size="small"
|
|
@@ -292,8 +292,8 @@
|
|
|
292
292
|
<EmptyState
|
|
293
293
|
message="Nothing to show here"
|
|
294
294
|
hint={filter === 'answered'
|
|
295
|
-
? 'No
|
|
296
|
-
: 'Every
|
|
295
|
+
? 'No field has a response yet. Start the experience on the page.'
|
|
296
|
+
: 'Every field this experience asks for already has a response.'}
|
|
297
297
|
/>
|
|
298
298
|
{:else}
|
|
299
299
|
<ul class="rows" data-testid="devtools-answers">
|
|
@@ -330,7 +330,7 @@
|
|
|
330
330
|
title={withheld}
|
|
331
331
|
onclick={() => startEdit(row)}
|
|
332
332
|
>
|
|
333
|
-
{row.view.answered ? 'Change' : '
|
|
333
|
+
{row.view.answered ? 'Change' : 'Add'}
|
|
334
334
|
</Button>
|
|
335
335
|
{#if row.view.answered}
|
|
336
336
|
<Button
|
|
@@ -346,7 +346,7 @@
|
|
|
346
346
|
</span>
|
|
347
347
|
{/if}
|
|
348
348
|
{:else}
|
|
349
|
-
<Badge status="default" size="small">Change it on its
|
|
349
|
+
<Badge status="default" size="small">Change it on its step</Badge>
|
|
350
350
|
{/if}
|
|
351
351
|
</div>
|
|
352
352
|
|
|
@@ -388,7 +388,7 @@
|
|
|
388
388
|
class="inline-input"
|
|
389
389
|
type={row.view.kind === 'number' ? 'number' : 'text'}
|
|
390
390
|
bind:value={draft}
|
|
391
|
-
placeholder={row.field.placeholder ?? 'Type
|
|
391
|
+
placeholder={row.field.placeholder ?? 'Type a response'}
|
|
392
392
|
aria-label={row.view.title}
|
|
393
393
|
/>
|
|
394
394
|
{/if}
|
|
@@ -405,7 +405,7 @@
|
|
|
405
405
|
{:else if row.view.answered && row.view.display !== null}
|
|
406
406
|
<Text size="small" color="subdued">{row.view.display}</Text>
|
|
407
407
|
{:else}
|
|
408
|
-
<Text size="small" color="subdued">
|
|
408
|
+
<Text size="small" color="subdued">No response yet</Text>
|
|
409
409
|
{/if}
|
|
410
410
|
</li>
|
|
411
411
|
{/each}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* @param {GuideSection} section - The section to read.
|
|
25
25
|
* @returns {{ id: string; label: string } | null} The tab to open and its name.
|
|
26
26
|
* @example
|
|
27
|
-
* openTarget({ ...section, tab: 'answers' }) // { id: 'answers', label: '
|
|
27
|
+
* openTarget({ ...section, tab: 'answers' }) // { id: 'answers', label: 'Responses' }
|
|
28
28
|
*/
|
|
29
29
|
function openTarget(section: GuideSection): { id: string; label: string } | null {
|
|
30
30
|
const id = section.tab
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file The review tabs' panels: How to use, This session,
|
|
3
|
-
*
|
|
2
|
+
* @file The review tabs' panels: How to use, This session, Responses, Flow, Personas & Scenarios
|
|
3
|
+
* and Feature flags.
|
|
4
4
|
*/
|
|
5
5
|
export { default as GuidePanel } from './guide/GuidePanel.svelte';
|
|
6
6
|
export { default as SessionPanel } from './session/SessionPanel.svelte';
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* @file
|
|
3
|
+
* @file An experience's flow drawn as a tree — the Developer group's experience tree, in plain
|
|
4
|
+
* English.
|
|
4
5
|
*
|
|
5
6
|
* `derive/journey-tree` does the thinking: the graph-to-tree walk and the tidy layout. This file
|
|
6
7
|
* owns nothing but pan, zoom and selection, which is why the layout is testable and this is not.
|
|
7
|
-
* The look follows the experience tree: left to right, a coloured dot per node (grey
|
|
8
|
-
*
|
|
8
|
+
* The look follows the experience tree: left to right, a coloured dot per node (grey origin, blue
|
|
9
|
+
* step, amber condition, sky-blue for a branch that rejoins), curved links, a condition's arms
|
|
9
10
|
* green and red, and each label above its dot — below it at the end of a branch.
|
|
10
11
|
*
|
|
11
12
|
* NODES ARE HTML, LINKS ARE SVG. A node is a real `<button>` holding its dot and label,
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
* ACTIONS ARE NOT DRAWN. A route runs straight across them, and where a branch hands off to one
|
|
24
25
|
* and stops, the pop-out names it on the node before.
|
|
25
26
|
*/
|
|
27
|
+
import { NODE_LABELS } from '../../derive'
|
|
26
28
|
import type { TreeLayout, TreeNode, TreeNodeKind } from '../../derive'
|
|
27
29
|
|
|
28
30
|
interface JourneyMapProps {
|
|
@@ -50,11 +52,11 @@
|
|
|
50
52
|
const POP_EDGE = 8
|
|
51
53
|
|
|
52
54
|
const KIND_LABELS: Record<TreeNodeKind, string> = {
|
|
53
|
-
start:
|
|
54
|
-
screen:
|
|
55
|
-
decision:
|
|
56
|
-
system:
|
|
57
|
-
journey:
|
|
55
|
+
start: NODE_LABELS.origin,
|
|
56
|
+
screen: NODE_LABELS.step,
|
|
57
|
+
decision: NODE_LABELS.condition,
|
|
58
|
+
system: NODE_LABELS.action,
|
|
59
|
+
journey: NODE_LABELS.experience,
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
let viewport: HTMLDivElement | undefined = $state()
|
|
@@ -554,7 +556,7 @@
|
|
|
554
556
|
{#if end.armLabel !== null}
|
|
555
557
|
<span class="arm-name arm-{end.arm ?? 'none'}">{end.armLabel}</span>
|
|
556
558
|
{/if}
|
|
557
|
-
<span>Ends: {end.label} (
|
|
559
|
+
<span>Ends: {end.label} (action)</span>
|
|
558
560
|
</li>
|
|
559
561
|
{/each}
|
|
560
562
|
</ul>
|
|
@@ -582,7 +584,7 @@
|
|
|
582
584
|
</button>
|
|
583
585
|
{:else if canJump(selectedNode)}
|
|
584
586
|
<button type="button" class="pop-action" onclick={() => goBack(selectedNode)}>
|
|
585
|
-
Go back to this
|
|
587
|
+
Go back to this step
|
|
586
588
|
</button>
|
|
587
589
|
{/if}
|
|
588
590
|
</div>
|
|
@@ -590,10 +592,10 @@
|
|
|
590
592
|
</div>
|
|
591
593
|
|
|
592
594
|
<p class="legend">
|
|
593
|
-
<span class="key kind-start"></span>
|
|
594
|
-
<span class="key kind-screen"></span>
|
|
595
|
-
<span class="key kind-decision"></span>
|
|
596
|
-
<span class="key kind-journey"></span>
|
|
595
|
+
<span class="key kind-start"></span> {KIND_LABELS.start}
|
|
596
|
+
<span class="key kind-screen"></span> {KIND_LABELS.screen}
|
|
597
|
+
<span class="key kind-decision"></span> {KIND_LABELS.decision}
|
|
598
|
+
<span class="key kind-journey"></span> {KIND_LABELS.journey}
|
|
597
599
|
<span class="key reference"></span> Rejoins an earlier branch (dashed)
|
|
598
600
|
<span class="key seen"></span> Seen
|
|
599
601
|
<span class="key current"></span> You are here
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* @file The
|
|
4
|
-
* experience tree without the node ids or the raw JSON Logic. It opens on the
|
|
5
|
-
* of the reviewer, and the picker reaches every other experience in the workspace.
|
|
3
|
+
* @file The Flow tab: any experience's flow in plain English — the Developer group's
|
|
4
|
+
* experience tree without the node ids or the raw JSON Logic. It opens on the experience in
|
|
5
|
+
* front of the reviewer, and the picker reaches every other experience in the workspace.
|
|
6
6
|
*
|
|
7
|
-
* Navigation is BACKWARDS ONLY, and only in the
|
|
8
|
-
* a plain `goToStepByKey`: the flow has been through every action in between, so returning to
|
|
9
|
-
* is safe. A
|
|
10
|
-
* make it renderable, and the
|
|
11
|
-
*
|
|
7
|
+
* Navigation is BACKWARDS ONLY, and only in the experience that is OPEN. A step already visited
|
|
8
|
+
* is a plain `goToStepByKey`: the flow has been through every action in between, so returning to
|
|
9
|
+
* it is safe. A step AHEAD is rendered but not clickable — jumping forward skips the actions that
|
|
10
|
+
* make it renderable, and the step it lands on can render an error state no visitor could reach.
|
|
11
|
+
* Forward motion belongs to the experience itself, or to a custom tool that replays one.
|
|
12
12
|
*
|
|
13
13
|
* THREE VIEWS, ONE GRAPH. The outline answers "where am I, and why did it go there"; the map
|
|
14
14
|
* answers "what shape is this, and where does each branch end"; the timeline answers "in what
|
|
15
15
|
* order, and where do the branches meet again". All three read the same `treeStore` through the
|
|
16
|
-
* same vocabulary modules, so a
|
|
16
|
+
* same vocabulary modules, so a step is named identically in each and they cannot drift.
|
|
17
17
|
*/
|
|
18
18
|
import type { Picked } from '@picked/client'
|
|
19
19
|
import { Badge, Banner, Button, Card, EmptyState } from '../../ui'
|
|
@@ -155,14 +155,14 @@
|
|
|
155
155
|
* @param {OutlineRow} row - The row to explain.
|
|
156
156
|
* @returns {string | undefined} The reason, or undefined when there is nothing to explain.
|
|
157
157
|
* @example
|
|
158
|
-
* jumpReason(aheadRow) // "
|
|
158
|
+
* jumpReason(aheadRow) // "Your responses don't reach this step yet."
|
|
159
159
|
*/
|
|
160
160
|
function jumpReason(row: OutlineRow): string | undefined {
|
|
161
161
|
if (row.kind !== 'screen') return undefined
|
|
162
162
|
if (posture.reason !== null) return posture.reason
|
|
163
|
-
if (!isOpen) return "This
|
|
164
|
-
if (row.state === 'current') return "You're on this
|
|
165
|
-
if (row.state === 'ahead') return "
|
|
163
|
+
if (!isOpen) return "This experience isn't open, so there is nowhere to go back to."
|
|
164
|
+
if (row.state === 'current') return "You're on this step."
|
|
165
|
+
if (row.state === 'ahead') return "Your responses don't reach this step yet."
|
|
166
166
|
return undefined
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -219,13 +219,13 @@
|
|
|
219
219
|
<div class="journey">
|
|
220
220
|
{#if selectedKey === null}
|
|
221
221
|
<EmptyState
|
|
222
|
-
message="No
|
|
222
|
+
message="No experiences yet"
|
|
223
223
|
hint="The workspace has not loaded its experiences. Reload the page if this does not change."
|
|
224
224
|
/>
|
|
225
225
|
{:else}
|
|
226
226
|
<!-- One row: how to look on the left, what to look at on the right. -->
|
|
227
227
|
<div class="controls">
|
|
228
|
-
<div class="views" role="group" aria-label="
|
|
228
|
+
<div class="views" role="group" aria-label="Flow view">
|
|
229
229
|
<button
|
|
230
230
|
type="button"
|
|
231
231
|
class="view"
|
|
@@ -261,12 +261,12 @@
|
|
|
261
261
|
<div class="picker">
|
|
262
262
|
{#if followed !== null && !isOpen}
|
|
263
263
|
<Button variant="ghost" size="small" onclick={() => (picked = null)}>
|
|
264
|
-
Back to the open
|
|
264
|
+
Back to the open experience
|
|
265
265
|
</Button>
|
|
266
266
|
{/if}
|
|
267
267
|
<select
|
|
268
268
|
class="picker-select"
|
|
269
|
-
aria-label="
|
|
269
|
+
aria-label="Experience"
|
|
270
270
|
value={selectedKey}
|
|
271
271
|
onchange={pick}
|
|
272
272
|
data-testid="devtools-journey-picker"
|
|
@@ -287,8 +287,8 @@
|
|
|
287
287
|
{#if rows.length === 0}
|
|
288
288
|
<Card sectioned>
|
|
289
289
|
<EmptyState
|
|
290
|
-
message="This
|
|
291
|
-
hint="Its flow has not loaded, or it is built entirely from
|
|
290
|
+
message="This experience has no steps yet"
|
|
291
|
+
hint="Its flow has not loaded, or it is built entirely from actions."
|
|
292
292
|
/>
|
|
293
293
|
</Card>
|
|
294
294
|
{:else if layout !== null}
|
|
@@ -343,7 +343,7 @@
|
|
|
343
343
|
{:else if row.kind === 'arm'}
|
|
344
344
|
<span class="arm">{row.label}</span>
|
|
345
345
|
{:else if row.kind === 'end'}
|
|
346
|
-
<span class="end-row">⇥ Ends: {row.label} (
|
|
346
|
+
<span class="end-row">⇥ Ends: {row.label} (action)</span>
|
|
347
347
|
{:else if row.kind === 'journey'}
|
|
348
348
|
<span class="journey-row">
|
|
349
349
|
<span class="marker muted" aria-hidden="true"></span>
|