@puzzmo/sdk 1.0.9 → 1.0.11

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/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./objectSpread2-C4RR_HMd.cjs`);function t(e){let t=e>=3600*1e3;return new Date(e).toISOString().slice(t?11:14,-1).split(`.`)[0]}function n(e=0,n=0){let r=e,i=n,a=0,o,s,c,l=()=>{var e;if(s===void 0)return r+i;if(o!==void 0)return o;let t=((e=c)==null?performance.now():e)-s-a;return r+i+t};return{_init:()=>{s===void 0&&(s=performance.now(),c=void 0)},_pause:()=>{c!==void 0||s===void 0||(c=performance.now())},_resume:()=>{c!==void 0&&(a+=performance.now()-c,c=void 0)},_reset:(e=0,t=0)=>{r=e,i=t,a=0,o=void 0,s=void 0,c=void 0},_conclude:()=>{if(c!==void 0){o=l();return}if(s===void 0){o=r+i;return}o=l()},timeMs:()=>l(),timeSecs:()=>l()/1e3,addedTimeMs:()=>i,addedTimeSecs:()=>i/1e3,timeWithoutPenaltySecs:()=>(l()-i)/1e3,addPenalty:e=>{i+=e},isPaused:()=>c!==void 0||s===void 0,isRunning:()=>s!==void 0&&c===void 0,display:()=>{let e=l()-i;return[t(Math.max(0,e)),i===0?``:t(i)]}}}function r(){let e=new Map;return typeof window<`u`&&window.addEventListener(`message`,t=>{var n;if(!(!(t==null||(n=t.data)==null)&&n.type))return;let r=t.data.type,i=e.get(r);if(i){var a,o;let e=(a=(o=t.data.data)==null?t.data.json:o)==null?{}:a;r!==`TIMER_TICK`&&r!==`TIMER_SYNC`&&console.log(`[Puzzmo SDK] received:`,r,e),i.forEach(t=>t(e))}}),{sendMessage:(e,t)=>{var n,r;let i={type:e,json:t,_:`p`,__:`mp`,private:!0};`parent`in window&&window.parent!==window&&window.parent.postMessage(i,`*`),window.postMessage(i,`*`),`webkit`in window&&!((n=window.webkit)==null||(n=n.messageHandlers)==null)&&n.app&&window.webkit.messageHandlers.app.postMessage(i),`puzzmoMessageString`in window&&window.puzzmoMessageString(JSON.stringify(i)),`ReactNativeWebView`in window&&(r=window.ReactNativeWebView)!=null&&r.postMessage&&window.ReactNativeWebView.postMessage(JSON.stringify(i)),e!==`TIMER_TICK`&&e!==`TIMER_SYNC`&&console.log(`[Puzzmo SDK] sent:`,e,t)},onMessage:(t,n)=>(e.has(t)||e.set(t,new Set),e.get(t).add(n),()=>{var r;(r=e.get(t))==null||r.delete(n)})}}var i=r();const a=(t={})=>{let r=null,a=null,o=()=>{var e;return r==null||(e=r.startOrFindGameplay)==null?void 0:e.gamePlayed},s=()=>{var e,t;return(e=(t=o())==null?void 0:t.id)==null?null:e},c=()=>{var e,t;return(e=(t=o())==null?void 0:t.puzzle.puzzle)==null?null:e},l=()=>{var e,t;return(e=(t=o())==null?void 0:t.boardState)==null?null:e},u=()=>{var e;return(e=r==null?void 0:r.theme)==null?null:e},d=()=>{var e,t;return(e=(t=o())==null?void 0:t.completed)==null?!1:e},f=new Map,p=n(),m=null,h=null,g=()=>{m||(m=setInterval(()=>{if(p.isPaused())return;let[e,t]=p.display();i.sendMessage(`TIMER_TICK`,{display:[e,t]})},500),h=setInterval(()=>{p.isPaused()||i.sendMessage(`TIMER_SYNC`,Math.floor(p.timeWithoutPenaltySecs()))},1e4))},_=()=>{m&&(clearInterval(m),m=null),h&&(clearInterval(h),h=null)},v=(e,t)=>{let n=f.get(e);n&&n.forEach(e=>e(t))};return i.onMessage(`START_GAME`,()=>{p._init(),g(),v(`start`)}),i.onMessage(`PAUSE_GAME`,()=>{p._pause(),v(`pause`)}),i.onMessage(`RESUME_GAME`,()=>{p._resume(),v(`resume`)}),i.onMessage(`SETTINGS_UPDATE`,e=>v(`settingsUpdate`,e)),i.onMessage(`RETRY_PUZZLE`,()=>{p._reset(),_(),v(`retry`)}),i.onMessage(`READY_DATA`,e=>{var t;let n=e;r=n;let i=(t=n.startOrFindGameplay)==null?void 0:t.gamePlayed;if(i){var o,s;let e=((o=i.elapsedTimeSecs)==null?0:o)*1e3,t=((s=i.additionalTimeAddedSecs)==null?0:s)*1e3;p._reset(e,t)}a&&(a(n),a=null)}),{timer:{timeMs:()=>p.timeMs(),timeSecs:()=>p.timeSecs(),addedTimeMs:()=>p.addedTimeMs(),addedTimeSecs:()=>p.addedTimeSecs(),timeWithoutPenaltySecs:()=>p.timeWithoutPenaltySecs(),display:()=>p.display(),addPenalty:e=>p.addPenalty(e),isPaused:()=>p.isPaused(),isRunning:()=>p.isRunning()},gameReady:function(){var n=e.n(function*(){var e;if(i.sendMessage(`READY`,{}),c())return{puzzleString:c(),boardState:l(),theme:u(),completed:d(),readyData:r};let n=(e=t.timeout)==null?5e3:e;yield new Promise((e,t)=>{a=e,setTimeout(()=>{a&&(a=null,t(Error(`Timeout waiting for READY_DATA after ${n}ms`)))},n)});let o=c();if(!o)throw Error(`READY_DATA received but no puzzle data found`);return{puzzleString:o,boardState:l(),theme:u(),completed:d(),readyData:r}});return function(){return n.apply(this,arguments)}}(),gameLoaded:(e={})=>{i.sendMessage(`READY_GAME_LOADED`,{state:e,gameRuntimeContract:`1.0`,embedRuntimeContract:`1.0`})},on:(e,t)=>(f.has(e)||f.set(e,new Set),f.get(e).add(t),()=>{var n;(n=f.get(e))==null||n.delete(t)}),off:(e,t)=>{var n;(n=f.get(e))==null||n.delete(t)},updateGameState:(e,t)=>{var n,r;let a=s();a&&i.sendMessage(`UPLOAD_NEW_GAME_STATE`,{id:a,input:{boardState:e,elapsedTimeSecs:(n=t==null?void 0:t.elapsedTimeSecs)==null?p.timeWithoutPenaltySecs():n,additionalTimeAddedSecs:(r=t==null?void 0:t.additionalTimeAddedSecs)==null?p.addedTimeSecs():r,collabUserReferences:[]}})},gameCompleted:(t,n)=>{var r,a,o,c,l;p._conclude(),_();let u=e.t(e.t({},t),{},{elapsedTimeSecs:(r=t.elapsedTimeSecs)==null?p.timeWithoutPenaltySecs():r,additionalTimeAddedSecs:(a=t.additionalTimeAddedSecs)==null?p.addedTimeSecs():a}),d=(o=n==null?void 0:n.deeds)==null?[]:o;d.push({id:`points`,value:t.pointsAwarded}),d.push({id:`time`,value:Math.round((c=u.elapsedTimeSecs)==null?0:c)+Math.round((l=u.additionalTimeAddedSecs)==null?0:l)});let f=s();f&&i.sendMessage(`GAME_COMPLETED`,{id:f,input:u,config:n})},showCompletionScreen:(e,t,n=!0)=>{i.sendMessage(`SHOW_GAME_COMPLETE_SCREEN`,{results:e,showRetry:n,gameplay:t})},hitCheckpoint:(e,t,n)=>{var r;if(!s())return;let a=(r=l())==null?``:r,o={elapsedTimeSecs:p.timeWithoutPenaltySecs(),additionalTimeAddedSecs:p.addedTimeSecs()};i.sendMessage(`HIT_CHECKPOINT`,{checkpointName:e,gameplay:{inputStr:a,play:o},checkpointConfig:t,augConfig:n==null?{}:n})},_hostAPI:i}};var o=class extends Error{constructor(e,t,n){super(t),this.type=e,this.originalError=n,this.name=`EditorImportError`}};exports.EditorImportError=o,exports.createPuzzmoSDK=a;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./objectSpread2-C4RR_HMd.cjs`);function t(e){let t=e>=3600*1e3;return new Date(e).toISOString().slice(t?11:14,-1).split(`.`)[0]}function n(e=0,n=0){let r=e,i=n,a=0,o,s,c,l=()=>{var e;if(s===void 0)return r+i;if(o!==void 0)return o;let t=((e=c)==null?performance.now():e)-s-a;return r+i+t};return{_init:()=>{s===void 0&&(s=performance.now(),c=void 0)},_pause:()=>{c!==void 0||s===void 0||(c=performance.now())},_resume:()=>{c!==void 0&&(a+=performance.now()-c,c=void 0)},_reset:(e=0,t=0)=>{r=e,i=t,a=0,o=void 0,s=void 0,c=void 0},_conclude:()=>{if(c!==void 0){o=l();return}if(s===void 0){o=r+i;return}o=l()},timeMs:()=>l(),timeSecs:()=>l()/1e3,addedTimeMs:()=>i,addedTimeSecs:()=>i/1e3,timeWithoutPenaltySecs:()=>(l()-i)/1e3,addPenalty:e=>{i+=e},isPaused:()=>c!==void 0||s===void 0,isRunning:()=>s!==void 0&&c===void 0,display:()=>{let e=l()-i;return[t(Math.max(0,e)),i===0?``:t(i)]}}}function r(){let e=new Map;return typeof window<`u`&&window.addEventListener(`message`,t=>{var n;if(!(!(t==null||(n=t.data)==null)&&n.type))return;let r=t.data.type,i=e.get(r);if(i){var a,o;let e=(a=(o=t.data.data)==null?t.data.json:o)==null?{}:a;r!==`TIMER_TICK`&&r!==`TIMER_SYNC`&&console.log(`[Puzzmo SDK] received:`,r,e),i.forEach(t=>t(e))}}),{sendMessage:(e,t)=>{var n,r;let i={type:e,json:t,_:`p`,__:`mp`,private:!0};`parent`in window&&window.parent!==window&&window.parent.postMessage(i,`*`),window.postMessage(i,`*`),`webkit`in window&&!((n=window.webkit)==null||(n=n.messageHandlers)==null)&&n.app&&window.webkit.messageHandlers.app.postMessage(i),`puzzmoMessageString`in window&&window.puzzmoMessageString(JSON.stringify(i)),`ReactNativeWebView`in window&&(r=window.ReactNativeWebView)!=null&&r.postMessage&&window.ReactNativeWebView.postMessage(JSON.stringify(i)),e!==`TIMER_TICK`&&e!==`TIMER_SYNC`&&console.log(`[Puzzmo SDK] sent:`,e,t)},onMessage:(t,n)=>(e.has(t)||e.set(t,new Set),e.get(t).add(n),()=>{var r;(r=e.get(t))==null||r.delete(n)})}}var i=r();const a=(t={})=>{let r=null,a=null,o=()=>{var e;return r==null||(e=r.startOrFindGameplay)==null?void 0:e.gamePlayed},s=()=>{var e,t;return(e=(t=o())==null?void 0:t.id)==null?null:e},c=()=>{var e,t;return(e=(t=o())==null?void 0:t.puzzle.puzzle)==null?null:e},l=()=>{var e,t;return(e=(t=o())==null?void 0:t.boardState)==null?null:e},u=()=>{var e;return(e=r==null?void 0:r.theme)==null?null:e},d=()=>{var e,t;return(e=(t=o())==null?void 0:t.completed)==null?!1:e},f=new Map,p=n(),m=null,h=null,g=()=>{m||(m=setInterval(()=>{if(p.isPaused())return;let[e,t]=p.display();i.sendMessage(`TIMER_TICK`,{display:[e,t]})},500),h=setInterval(()=>{p.isPaused()||i.sendMessage(`TIMER_SYNC`,Math.floor(p.timeWithoutPenaltySecs()))},1e4))},_=()=>{m&&(clearInterval(m),m=null),h&&(clearInterval(h),h=null)},v=(e,t)=>{let n=f.get(e);n&&n.forEach(e=>e(t))};return i.onMessage(`START_GAME`,()=>{p._init(),g(),v(`start`)}),i.onMessage(`PAUSE_GAME`,()=>{p._pause(),v(`pause`)}),i.onMessage(`RESUME_GAME`,()=>{p._resume(),v(`resume`)}),i.onMessage(`SETTINGS_UPDATE`,e=>v(`settingsUpdate`,e)),i.onMessage(`KEYBOARD_KEY_PRESS`,e=>v(`keyboardKeyPress`,e)),i.onMessage(`KEYBOARD_CURSOR_CHANGE`,e=>v(`keyboardCursorChange`,e)),i.onMessage(`KEYBOARD_CURSOR_END`,()=>v(`keyboardCursorEnd`)),i.onMessage(`RETRY_PUZZLE`,()=>{p._reset(),_(),v(`retry`)}),i.onMessage(`READY_DATA`,e=>{var t;let n=e;r=n;let i=(t=n.startOrFindGameplay)==null?void 0:t.gamePlayed;if(i){var o,s;let e=((o=i.elapsedTimeSecs)==null?0:o)*1e3,t=((s=i.additionalTimeAddedSecs)==null?0:s)*1e3;p._reset(e,t)}a&&(a(n),a=null)}),{timer:{timeMs:()=>p.timeMs(),timeSecs:()=>p.timeSecs(),addedTimeMs:()=>p.addedTimeMs(),addedTimeSecs:()=>p.addedTimeSecs(),timeWithoutPenaltySecs:()=>p.timeWithoutPenaltySecs(),display:()=>p.display(),addPenalty:e=>p.addPenalty(e),isPaused:()=>p.isPaused(),isRunning:()=>p.isRunning()},gameReady:function(){var n=e.n(function*(){var e;if(i.sendMessage(`READY`,{}),c())return{puzzleString:c(),boardState:l(),theme:u(),completed:d(),readyData:r};let n=(e=t.timeout)==null?5e3:e;yield new Promise((e,t)=>{a=e,setTimeout(()=>{a&&(a=null,t(Error(`Timeout waiting for READY_DATA after ${n}ms`)))},n)});let o=c();if(!o)throw Error(`READY_DATA received but no puzzle data found`);return{puzzleString:o,boardState:l(),theme:u(),completed:d(),readyData:r}});return function(){return n.apply(this,arguments)}}(),gameLoaded:(e={})=>{i.sendMessage(`READY_GAME_LOADED`,{state:e,gameRuntimeContract:`1.0`,embedRuntimeContract:`1.0`})},on:(e,t)=>(f.has(e)||f.set(e,new Set),f.get(e).add(t),()=>{var n;(n=f.get(e))==null||n.delete(t)}),off:(e,t)=>{var n;(n=f.get(e))==null||n.delete(t)},updateGameState:(e,t)=>{var n,r;let a=s();a&&i.sendMessage(`UPLOAD_NEW_GAME_STATE`,{id:a,input:{boardState:e,elapsedTimeSecs:(n=t==null?void 0:t.elapsedTimeSecs)==null?p.timeWithoutPenaltySecs():n,additionalTimeAddedSecs:(r=t==null?void 0:t.additionalTimeAddedSecs)==null?p.addedTimeSecs():r,collabUserReferences:[]}})},gameCompleted:(t,n)=>{var r,a,o,c,l;p._conclude(),_();let u=e.t(e.t({},t),{},{elapsedTimeSecs:(r=t.elapsedTimeSecs)==null?p.timeWithoutPenaltySecs():r,additionalTimeAddedSecs:(a=t.additionalTimeAddedSecs)==null?p.addedTimeSecs():a}),d=(o=n==null?void 0:n.deeds)==null?[]:o;d.push({id:`points`,value:t.pointsAwarded}),d.push({id:`time`,value:Math.round((c=u.elapsedTimeSecs)==null?0:c)+Math.round((l=u.additionalTimeAddedSecs)==null?0:l)});let f=s();f&&i.sendMessage(`GAME_COMPLETED`,{id:f,input:u,config:n})},showCompletionScreen:(e,t,n=!0)=>{i.sendMessage(`SHOW_GAME_COMPLETE_SCREEN`,{results:e,showRetry:n,gameplay:t})},hitCheckpoint:(e,t,n)=>{var r;if(!s())return;let a=(r=l())==null?``:r,o={elapsedTimeSecs:p.timeWithoutPenaltySecs(),additionalTimeAddedSecs:p.addedTimeSecs()};i.sendMessage(`HIT_CHECKPOINT`,{checkpointName:e,gameplay:{inputStr:a,play:o},checkpointConfig:t,augConfig:n==null?{}:n})},keyboard:{show:e=>{i.sendMessage(`KEYBOARD_UPDATE_CONFIG`,e)},hide:()=>{i.sendMessage(`KEYBOARD_UPDATE_CONFIG`,{layout:[],symbols:{},highlight:[],disabled:[],xl:[],l:[],supportsDragCursor:!1})}},_hostAPI:i}},o={layout:[`qwertyuiop`,`asdfghjkl`,`↵zxcvbnm⌫`,void 0],symbols:{"↵":`Enter`,"⌫":`bsp`},highlight:[`↵`,`⌫`],disabled:[],xl:[],l:[`↵`,`⌫`],supportsDragCursor:!1};var s=class extends Error{constructor(e,t,n){super(t),this.type=e,this.originalError=n,this.name=`EditorImportError`}};exports.EditorImportError=s,exports.createPuzzmoSDK=a,exports.defaultKeyboardConfig=o;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/sdk.ts","../src/editor.ts"],"sourcesContent":["import type { MessagesSentFromEmbed, MessagesReceived, GamePlay, AugmentationConfig, CheckpointConfig, Theme, Deed } from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n \"READY_DATA\" | \"START_GAME\" | \"PAUSE_GAME\" | \"RESUME_GAME\" | \"SETTINGS_UPDATE\" | \"RETRY_PUZZLE\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n boardState: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n return {\n puzzleString: getPuzzleString()!,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n return {\n puzzleString,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n _hostAPI: hostAPI,\n }\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/sdk.ts","../src/keyboard.ts","../src/editor.ts"],"sourcesContent":["import type {\n MessagesSentFromEmbed,\n MessagesReceived,\n GamePlay,\n AugmentationConfig,\n CheckpointConfig,\n Theme,\n Deed,\n KeyboardConfig,\n} from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n | \"KEYBOARD_UPDATE_CONFIG\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n | \"READY_DATA\"\n | \"START_GAME\"\n | \"PAUSE_GAME\"\n | \"RESUME_GAME\"\n | \"SETTINGS_UPDATE\"\n | \"RETRY_PUZZLE\"\n | \"KEYBOARD_KEY_PRESS\"\n | \"KEYBOARD_CURSOR_CHANGE\"\n | \"KEYBOARD_CURSOR_END\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n /** A key on the on-screen keyboard was tapped. */\n keyboardKeyPress: { key: string }\n /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */\n keyboardCursorChange: { position: [number, number] }\n /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */\n keyboardCursorEnd: void\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"KEYBOARD_KEY_PRESS\", (data) => emit(\"keyboardKeyPress\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_CHANGE\", (data) => emit(\"keyboardCursorChange\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_END\", () => emit(\"keyboardCursorEnd\"))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n boardState: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n return {\n puzzleString: getPuzzleString()!,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n return {\n puzzleString,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n keyboard: {\n /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */\n show: (config: KeyboardConfig) => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", config)\n },\n /** Hide the on-screen keyboard. */\n hide: () => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", {\n layout: [],\n symbols: {},\n highlight: [],\n disabled: [],\n xl: [],\n l: [],\n supportsDragCursor: false,\n })\n },\n },\n\n _hostAPI: hostAPI,\n }\n}\n","import type { KeyboardConfig } from \"./types\"\n\n/**\n * A standard QWERTY layout with Enter and Backspace — a reasonable default for\n * any game that needs text input. Customize from here by spreading and overriding.\n *\n * @example\n * // Use as-is\n * sdk.keyboard.show(defaultKeyboardConfig)\n *\n * @example\n * // Extend with dynamic disabled letters\n * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })\n */\nexport const defaultKeyboardConfig: KeyboardConfig = {\n layout: [\"qwertyuiop\", \"asdfghjkl\", \"↵zxcvbnm⌫\", undefined],\n symbols: { \"↵\": \"Enter\", \"⌫\": \"bsp\" },\n highlight: [\"↵\", \"⌫\"],\n disabled: [],\n xl: [],\n l: [\"↵\", \"⌫\"],\n supportsDragCursor: false,\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { createPuzzmoSDK } from "./sdk";
2
+ export { defaultKeyboardConfig } from "./keyboard";
2
3
  export type { SDK as PuzzmoSDK, PuzzmoSDKOptions, SDKEventMap, SDKEventType, SDKTimer } from "./sdk";
3
- export type { Theme, GamePlay, AugmentationConfig, CheckpointConfig, Deed, GameOverMessageUIComponent, BootstrapGameData, MessagesReceived, MessagesSentFromEmbed, ThumbnailConfig, } from "./types";
4
+ export type { Theme, GamePlay, AugmentationConfig, CheckpointConfig, Deed, GameOverMessageUIComponent, BootstrapGameData, MessagesReceived, MessagesSentFromEmbed, ThumbnailConfig, KeyboardConfig, } from "./types";
4
5
  export type { ValidationIssue, ValidationReport, ImportErrorType, ImportResult, EditorBundle } from "./editor";
5
6
  export { EditorImportError } from "./editor";
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AAEvC,YAAY,EAAE,GAAG,IAAI,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEpG,YAAY,EACV,KAAK,EACL,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,IAAI,EACJ,0BAA0B,EAC1B,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,GAChB,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE9G,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD,YAAY,EAAE,GAAG,IAAI,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEpG,YAAY,EACV,KAAK,EACL,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,IAAI,EACJ,0BAA0B,EAC1B,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE9G,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA"}
package/dist/index.js CHANGED
@@ -121,7 +121,7 @@ const o = (n = {}) => {
121
121
  m._pause(), y("pause");
122
122
  }), a.onMessage("RESUME_GAME", () => {
123
123
  m._resume(), y("resume");
124
- }), a.onMessage("SETTINGS_UPDATE", (e) => y("settingsUpdate", e)), a.onMessage("RETRY_PUZZLE", () => {
124
+ }), a.onMessage("SETTINGS_UPDATE", (e) => y("settingsUpdate", e)), a.onMessage("KEYBOARD_KEY_PRESS", (e) => y("keyboardKeyPress", e)), a.onMessage("KEYBOARD_CURSOR_CHANGE", (e) => y("keyboardCursorChange", e)), a.onMessage("KEYBOARD_CURSOR_END", () => y("keyboardCursorEnd")), a.onMessage("RETRY_PUZZLE", () => {
125
125
  m._reset(), v(), y("retry");
126
126
  }), a.onMessage("READY_DATA", (e) => {
127
127
  var t;
@@ -249,15 +249,47 @@ const o = (n = {}) => {
249
249
  augConfig: n == null ? {} : n
250
250
  });
251
251
  },
252
+ keyboard: {
253
+ show: (e) => {
254
+ a.sendMessage("KEYBOARD_UPDATE_CONFIG", e);
255
+ },
256
+ hide: () => {
257
+ a.sendMessage("KEYBOARD_UPDATE_CONFIG", {
258
+ layout: [],
259
+ symbols: {},
260
+ highlight: [],
261
+ disabled: [],
262
+ xl: [],
263
+ l: [],
264
+ supportsDragCursor: !1
265
+ });
266
+ }
267
+ },
252
268
  _hostAPI: a
253
269
  };
270
+ }, s = {
271
+ layout: [
272
+ "qwertyuiop",
273
+ "asdfghjkl",
274
+ "↵zxcvbnm⌫",
275
+ void 0
276
+ ],
277
+ symbols: {
278
+ "↵": "Enter",
279
+ "⌫": "bsp"
280
+ },
281
+ highlight: ["↵", "⌫"],
282
+ disabled: [],
283
+ xl: [],
284
+ l: ["↵", "⌫"],
285
+ supportsDragCursor: !1
254
286
  };
255
287
  /** Custom error class for workshop import failures */
256
- var s = class extends Error {
288
+ var c = class extends Error {
257
289
  constructor(e, t, n) {
258
290
  super(t), this.type = e, this.originalError = n, this.name = "EditorImportError";
259
291
  }
260
292
  };
261
- export { s as EditorImportError, o as createPuzzmoSDK };
293
+ export { c as EditorImportError, o as createPuzzmoSDK, s as defaultKeyboardConfig };
262
294
 
263
295
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/sdk.ts","../src/editor.ts"],"sourcesContent":["import type { MessagesSentFromEmbed, MessagesReceived, GamePlay, AugmentationConfig, CheckpointConfig, Theme, Deed } from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n \"READY_DATA\" | \"START_GAME\" | \"PAUSE_GAME\" | \"RESUME_GAME\" | \"SETTINGS_UPDATE\" | \"RETRY_PUZZLE\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n boardState: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n return {\n puzzleString: getPuzzleString()!,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n return {\n puzzleString,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n _hostAPI: hostAPI,\n }\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/sdk.ts","../src/keyboard.ts","../src/editor.ts"],"sourcesContent":["import type {\n MessagesSentFromEmbed,\n MessagesReceived,\n GamePlay,\n AugmentationConfig,\n CheckpointConfig,\n Theme,\n Deed,\n KeyboardConfig,\n} from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n | \"KEYBOARD_UPDATE_CONFIG\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n | \"READY_DATA\"\n | \"START_GAME\"\n | \"PAUSE_GAME\"\n | \"RESUME_GAME\"\n | \"SETTINGS_UPDATE\"\n | \"RETRY_PUZZLE\"\n | \"KEYBOARD_KEY_PRESS\"\n | \"KEYBOARD_CURSOR_CHANGE\"\n | \"KEYBOARD_CURSOR_END\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n /** A key on the on-screen keyboard was tapped. */\n keyboardKeyPress: { key: string }\n /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */\n keyboardCursorChange: { position: [number, number] }\n /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */\n keyboardCursorEnd: void\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"KEYBOARD_KEY_PRESS\", (data) => emit(\"keyboardKeyPress\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_CHANGE\", (data) => emit(\"keyboardCursorChange\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_END\", () => emit(\"keyboardCursorEnd\"))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n boardState: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n return {\n puzzleString: getPuzzleString()!,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n return {\n puzzleString,\n boardState: getBoardState(),\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n keyboard: {\n /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */\n show: (config: KeyboardConfig) => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", config)\n },\n /** Hide the on-screen keyboard. */\n hide: () => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", {\n layout: [],\n symbols: {},\n highlight: [],\n disabled: [],\n xl: [],\n l: [],\n supportsDragCursor: false,\n })\n },\n },\n\n _hostAPI: hostAPI,\n }\n}\n","import type { KeyboardConfig } from \"./types\"\n\n/**\n * A standard QWERTY layout with Enter and Backspace — a reasonable default for\n * any game that needs text input. Customize from here by spreading and overriding.\n *\n * @example\n * // Use as-is\n * sdk.keyboard.show(defaultKeyboardConfig)\n *\n * @example\n * // Extend with dynamic disabled letters\n * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })\n */\nexport const defaultKeyboardConfig: KeyboardConfig = {\n layout: [\"qwertyuiop\", \"asdfghjkl\", \"↵zxcvbnm⌫\", undefined],\n symbols: { \"↵\": \"Enter\", \"⌫\": \"bsp\" },\n highlight: [\"↵\", \"⌫\"],\n disabled: [],\n xl: [],\n l: [\"↵\", \"⌫\"],\n supportsDragCursor: false,\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { KeyboardConfig } from "./types";
2
+ /**
3
+ * A standard QWERTY layout with Enter and Backspace — a reasonable default for
4
+ * any game that needs text input. Customize from here by spreading and overriding.
5
+ *
6
+ * @example
7
+ * // Use as-is
8
+ * sdk.keyboard.show(defaultKeyboardConfig)
9
+ *
10
+ * @example
11
+ * // Extend with dynamic disabled letters
12
+ * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })
13
+ */
14
+ export declare const defaultKeyboardConfig: KeyboardConfig;
15
+ //# sourceMappingURL=keyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAQnC,CAAA"}
package/dist/sdk.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import type { MessagesSentFromEmbed, MessagesReceived, GamePlay, AugmentationConfig, CheckpointConfig, Theme } from "./types";
1
+ import type { MessagesSentFromEmbed, MessagesReceived, GamePlay, AugmentationConfig, CheckpointConfig, Theme, KeyboardConfig } from "./types";
2
2
  export type SDK = ReturnType<typeof createPuzzmoSDK>;
3
3
  export interface PuzzmoSDKOptions {
4
4
  /** Optional timeout in ms to wait for READY_DATA (default: 5000) */
5
5
  timeout?: number;
6
6
  }
7
- type SupportedOutgoingMessages = Pick<MessagesSentFromEmbed, "READY" | "READY_GAME_LOADED" | "GAME_COMPLETED" | "SHOW_GAME_COMPLETE_SCREEN" | "TIMER_TICK" | "TIMER_SYNC" | "UPLOAD_NEW_GAME_STATE" | "HIT_CHECKPOINT">;
8
- type SupportedIncomingMessages = Pick<MessagesReceived, "READY_DATA" | "START_GAME" | "PAUSE_GAME" | "RESUME_GAME" | "SETTINGS_UPDATE" | "RETRY_PUZZLE">;
7
+ type SupportedOutgoingMessages = Pick<MessagesSentFromEmbed, "READY" | "READY_GAME_LOADED" | "GAME_COMPLETED" | "SHOW_GAME_COMPLETE_SCREEN" | "TIMER_TICK" | "TIMER_SYNC" | "UPLOAD_NEW_GAME_STATE" | "HIT_CHECKPOINT" | "KEYBOARD_UPDATE_CONFIG">;
8
+ type SupportedIncomingMessages = Pick<MessagesReceived, "READY_DATA" | "START_GAME" | "PAUSE_GAME" | "RESUME_GAME" | "SETTINGS_UPDATE" | "RETRY_PUZZLE" | "KEYBOARD_KEY_PRESS" | "KEYBOARD_CURSOR_CHANGE" | "KEYBOARD_CURSOR_END">;
9
9
  type MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void;
10
10
  export type SDKEventMap = {
11
11
  start: void;
@@ -13,6 +13,16 @@ export type SDKEventMap = {
13
13
  resume: void;
14
14
  retry: void;
15
15
  settingsUpdate: any;
16
+ /** A key on the on-screen keyboard was tapped. */
17
+ keyboardKeyPress: {
18
+ key: string;
19
+ };
20
+ /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */
21
+ keyboardCursorChange: {
22
+ position: [number, number];
23
+ };
24
+ /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */
25
+ keyboardCursorEnd: void;
16
26
  };
17
27
  export type SDKEventType = keyof SDKEventMap;
18
28
  export interface SDKTimer {
@@ -52,6 +62,12 @@ export declare const createPuzzmoSDK: (options?: PuzzmoSDKOptions) => {
52
62
  gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => void;
53
63
  showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry?: boolean) => void;
54
64
  hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => void;
65
+ keyboard: {
66
+ /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */
67
+ show: (config: KeyboardConfig) => void;
68
+ /** Hide the on-screen keyboard. */
69
+ hide: () => void;
70
+ };
55
71
  _hostAPI: {
56
72
  sendMessage: <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => void;
57
73
  onMessage: <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => () => void;
package/dist/sdk.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,EAEL,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,KAAK,yBAAyB,GAAG,IAAI,CACnC,qBAAqB,EACnB,OAAO,GACP,mBAAmB,GACnB,gBAAgB,GAChB,2BAA2B,GAC3B,YAAY,GACZ,YAAY,GACZ,uBAAuB,GACvB,gBAAgB,GAChB,wBAAwB,CAC3B,CAAA;AAED,KAAK,yBAAyB,GAAG,IAAI,CACnC,gBAAgB,EACd,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,iBAAiB,GACjB,cAAc,GACd,oBAAoB,GACpB,wBAAwB,GACxB,qBAAqB,CACxB,CAAA;AAED,KAAK,cAAc,CAAC,CAAC,SAAS,MAAM,yBAAyB,IAAI,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;AAE7G,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,IAAI,CAAA;IACX,KAAK,EAAE,IAAI,CAAA;IACX,MAAM,EAAE,IAAI,CAAA;IACZ,KAAK,EAAE,IAAI,CAAA;IACX,cAAc,EAAE,GAAG,CAAA;IACnB,kDAAkD;IAClD,gBAAgB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACjC,+FAA+F;IAC/F,oBAAoB,EAAE;QAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACpD,kFAAkF;IAClF,iBAAiB,EAAE,IAAI,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,WAAW,CAAA;AAE5C,MAAM,WAAW,QAAQ;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,MAAM,CAAA;IACpB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,MAAM,CAAA;IACtB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,MAAM,CAAA;IACzB,wCAAwC;IACxC,aAAa,EAAE,MAAM,MAAM,CAAA;IAC3B,oDAAoD;IACpD,sBAAsB,EAAE,MAAM,MAAM,CAAA;IACpC,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,uCAAuC;IACvC,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,OAAO,CAAA;IACvB,sCAAsC;IACtC,SAAS,EAAE,MAAM,OAAO,CAAA;CACzB;AAyID,2EAA2E;AAC3E,eAAO,MAAM,eAAe,GAAI,UAAS,gBAAqB;;qBA4GrC,OAAO,CAAC;QAC3B,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;QACzB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;QACnB,SAAS,EAAE,OAAO,CAAA;QAClB,SAAS,EAAE,gBAAgB,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;KACjD,CAAC;yBAsCkB,GAAG;SAQlB,CAAC,SAAS,YAAY,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;UAQzF,CAAC,SAAS,YAAY,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI;mCAIlD,MAAM,SAAS,OAAO,CAAC,QAAQ,CAAC;0BAezC,OAAO,CAAC,QAAQ,CAAC,WAAW,kBAAkB;oCA2BpC,GAAG,EAAE,YAAY,QAAQ;oCAQzB,MAAM,oBAAoB,gBAAgB,WAAW,kBAAkB;;QAmBrG,oHAAoH;uBACrG,cAAc;QAG7B,mCAAmC;;;;sBAlSlB,CAAC,SAAS,MAAM,yBAAyB,QAAQ,CAAC,QAAQ,yBAAyB,CAAC,CAAC,CAAC;oBAiBxF,CAAC,SAAS,MAAM,yBAAyB,QAAQ,CAAC,WAAW,cAAc,CAAC,CAAC,CAAC;;CAiSlG,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"CtrlView.d.ts","sourceRoot":"","sources":["../../../src/simulator/views/CtrlView.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,UAAU,CAAA;AAI/D,wBAAgB,cAAc,IAAI,aAAa,CAiJ9C"}
1
+ {"version":3,"file":"CtrlView.d.ts","sourceRoot":"","sources":["../../../src/simulator/views/CtrlView.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,UAAU,CAAA;AAI/D,wBAAgB,cAAc,IAAI,aAAa,CA4I9C"}