slidedeck shape installed, still minor difficulty in keyboard arrow transition between slides (last slide + wraparound)
This commit is contained in:
parent
2590a86352
commit
a0e73b0f9e
|
|
@ -9,6 +9,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@anthropic-ai/sdk": "^0.33.1",
|
||||||
"@daily-co/daily-js": "^0.60.0",
|
"@daily-co/daily-js": "^0.60.0",
|
||||||
"@daily-co/daily-react": "^0.20.0",
|
"@daily-co/daily-react": "^0.20.0",
|
||||||
"@tldraw/assets": "^3.6.0",
|
"@tldraw/assets": "^3.6.0",
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
"@types/markdown-it": "^14.1.1",
|
"@types/markdown-it": "^14.1.1",
|
||||||
"@types/marked": "^5.0.2",
|
"@types/marked": "^5.0.2",
|
||||||
"@vercel/analytics": "^1.2.2",
|
"@vercel/analytics": "^1.2.2",
|
||||||
|
"ai": "^4.1.0",
|
||||||
"cherry-markdown": "^0.8.57",
|
"cherry-markdown": "^0.8.57",
|
||||||
"cloudflare-workers-unfurl": "^0.0.7",
|
"cloudflare-workers-unfurl": "^0.0.7",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
|
|
@ -28,6 +30,7 @@
|
||||||
"jspdf": "^2.5.2",
|
"jspdf": "^2.5.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"marked": "^15.0.4",
|
"marked": "^15.0.4",
|
||||||
|
"openai": "^4.79.3",
|
||||||
"rbush": "^4.0.1",
|
"rbush": "^4.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
@ -53,6 +56,90 @@
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ai-sdk/provider": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-lJi5zwDosvvZER3e/pB8lj1MN3o3S7zJliQq56BRr4e9V3fcRyFtwP0JRxaRS5vHYX3OJ154VezVoQNrk0eaKw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"json-schema": "^0.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ai-sdk/provider-utils": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-rBUabNoyB25PBUjaiMSk86fHNSCqTngNZVvXxv8+6mvw47JX5OexW+ZHRsEw8XKTE8+hqvNFVzctaOrRZ2i9Zw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider": "1.0.4",
|
||||||
|
"eventsource-parser": "^3.0.0",
|
||||||
|
"nanoid": "^3.3.8",
|
||||||
|
"secure-json-parse": "^2.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ai-sdk/react": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-U5lBbLyf1pw79xsk5dgHSkBv9Jta3xzWlOLpxsmHlxh1X94QOH3e1gm+nioQ/JvTuHLm23j2tz3i4MpMdchwXQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider-utils": "2.1.0",
|
||||||
|
"@ai-sdk/ui-utils": "1.1.0",
|
||||||
|
"swr": "^2.2.5",
|
||||||
|
"throttleit": "2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ai-sdk/ui-utils": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ETXwdHaHwzC7NIehbthDFGwsTFk+gNtRL/lm85nR4WDFvvYQptoM/7wTANs0p0H7zumB3Ep5hKzv0Encu8vSRw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider": "1.0.4",
|
||||||
|
"@ai-sdk/provider-utils": "2.1.0",
|
||||||
|
"zod-to-json-schema": "^3.24.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||||
|
|
@ -67,6 +154,30 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@anthropic-ai/sdk": {
|
||||||
|
"version": "0.33.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.33.1.tgz",
|
||||||
|
"integrity": "sha512-VrlbxiAdVRGuKP2UQlCnsShDHJKWepzvfRCkZMpU+oaUdKLpOfmylLMRojGrAgebV+kDtPjewCVP0laHXg+vsA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^18.11.18",
|
||||||
|
"@types/node-fetch": "^2.6.4",
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"agentkeepalive": "^4.2.1",
|
||||||
|
"form-data-encoder": "1.7.2",
|
||||||
|
"formdata-node": "^4.3.2",
|
||||||
|
"node-fetch": "^2.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
|
||||||
|
"version": "18.19.71",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.71.tgz",
|
||||||
|
"integrity": "sha512-evXpcgtZm8FY4jqBSN8+DmOTcVkkvTmAayeo4Wf3m1xAruyVGzGuDh/Fb/WWX2yLItUiho42ozyJjB0dw//Tkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.26.2",
|
"version": "7.26.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||||
|
|
@ -1318,6 +1429,15 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@opentelemetry/api": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/number": {
|
"node_modules/@radix-ui/number": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz",
|
||||||
|
|
@ -2765,6 +2885,12 @@
|
||||||
"integrity": "sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg==",
|
"integrity": "sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/diff-match-patch": {
|
||||||
|
"version": "1.0.36",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz",
|
||||||
|
"integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/dompurify": {
|
"node_modules/@types/dompurify": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz",
|
||||||
|
|
@ -2837,6 +2963,16 @@
|
||||||
"integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==",
|
"integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node-fetch": {
|
||||||
|
"version": "2.6.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||||
|
"integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"form-data": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node-forge": {
|
"node_modules/@types/node-forge": {
|
||||||
"version": "1.3.11",
|
"version": "1.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz",
|
||||||
|
|
@ -3387,6 +3523,18 @@
|
||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.14.0",
|
"version": "8.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||||
|
|
@ -3451,6 +3599,47 @@
|
||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/agentkeepalive": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"humanize-ms": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ai": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ai/-/ai-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-95nI9hBSSAKPrMnpJbaB3yqvh+G8BS4/EtFz3HR0HgEDJpxC0R6JAlB8+B/BXHd/roNGBrS08Z3Zain/6OFSYA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider": "1.0.4",
|
||||||
|
"@ai-sdk/provider-utils": "2.1.0",
|
||||||
|
"@ai-sdk/react": "1.1.0",
|
||||||
|
"@ai-sdk/ui-utils": "1.1.0",
|
||||||
|
"@opentelemetry/api": "1.9.0",
|
||||||
|
"jsondiffpatch": "0.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
|
@ -4742,6 +4931,12 @@
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/diff-match-patch": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/domexception": {
|
"node_modules/domexception": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
|
||||||
|
|
@ -5280,6 +5475,15 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eventemitter3": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
|
@ -5301,6 +5505,15 @@
|
||||||
"integrity": "sha512-blk1va0zol9QOrdZt0rFXo5KMkNPVSp92Eju/Qz8THwKWKRKeE0T8Br/1aW6+Edkyq9xHYgYxn2QtOnUKPUp+Q==",
|
"integrity": "sha512-blk1va0zol9QOrdZt0rFXo5KMkNPVSp92Eju/Qz8THwKWKRKeE0T8Br/1aW6+Edkyq9xHYgYxn2QtOnUKPUp+Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/eventsource-parser": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/execa": {
|
"node_modules/execa": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz",
|
||||||
|
|
@ -5438,6 +5651,25 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data-encoder": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/formdata-node": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-domexception": "1.0.0",
|
||||||
|
"web-streams-polyfill": "4.0.0-beta.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fp-ts": {
|
"node_modules/fp-ts": {
|
||||||
"version": "2.16.9",
|
"version": "2.16.9",
|
||||||
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.9.tgz",
|
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.9.tgz",
|
||||||
|
|
@ -5800,6 +6032,15 @@
|
||||||
"node": ">=8.12.0"
|
"node": ">=8.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/humanize-ms": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
|
@ -6076,6 +6317,12 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/json-schema": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||||
|
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
||||||
|
},
|
||||||
"node_modules/json-schema-to-ts": {
|
"node_modules/json-schema-to-ts": {
|
||||||
"version": "1.6.4",
|
"version": "1.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-1.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-1.6.4.tgz",
|
||||||
|
|
@ -6106,6 +6353,35 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsondiffpatch": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/diff-match-patch": "^1.0.36",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"diff-match-patch": "^1.0.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"jsondiffpatch": "bin/jsondiffpatch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsondiffpatch/node_modules/chalk": {
|
||||||
|
"version": "5.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||||
|
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
|
@ -6529,7 +6805,6 @@
|
||||||
"version": "3.3.8",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
@ -6544,6 +6819,25 @@
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-domexception": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.6.7",
|
"version": "2.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||||
|
|
@ -6707,6 +7001,45 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/openai": {
|
||||||
|
"version": "4.79.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/openai/-/openai-4.79.3.tgz",
|
||||||
|
"integrity": "sha512-0yAnr6oxXAyVrYwLC1jA0KboyU7DjEmrfTXQX+jSpE+P4i72AI/Lxx5pvR3r9i5X7G33835lL+ZrnQ+MDvyuUg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^18.11.18",
|
||||||
|
"@types/node-fetch": "^2.6.4",
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"agentkeepalive": "^4.2.1",
|
||||||
|
"form-data-encoder": "1.7.2",
|
||||||
|
"formdata-node": "^4.3.2",
|
||||||
|
"node-fetch": "^2.6.7"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"openai": "bin/cli"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"ws": "^8.18.0",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"ws": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openai/node_modules/@types/node": {
|
||||||
|
"version": "18.19.71",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.71.tgz",
|
||||||
|
"integrity": "sha512-evXpcgtZm8FY4jqBSN8+DmOTcVkkvTmAayeo4Wf3m1xAruyVGzGuDh/Fb/WWX2yLItUiho42ozyJjB0dw//Tkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/os-paths": {
|
"node_modules/os-paths": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/os-paths/-/os-paths-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/os-paths/-/os-paths-4.4.0.tgz",
|
||||||
|
|
@ -7498,6 +7831,12 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/secure-json-parse": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/selfsigned": {
|
"node_modules/selfsigned": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz",
|
||||||
|
|
@ -7803,6 +8142,19 @@
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/swr": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-NyZ76wA4yElZWBHzSgEJc28a0u6QZvhb6w0azeL2k7+Q1gAzVK+IqQYXhVOC/mzi+HZIozrZvBVeSeOZNR2bqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dequal": "^2.0.3",
|
||||||
|
"use-sync-external-store": "^1.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/symbol-tree": {
|
"node_modules/symbol-tree": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||||
|
|
@ -7836,6 +8188,18 @@
|
||||||
"utrie": "^1.0.2"
|
"utrie": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/throttleit": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/time-span": {
|
"node_modules/time-span": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/time-span/-/time-span-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/time-span/-/time-span-4.0.0.tgz",
|
||||||
|
|
@ -8066,6 +8430,12 @@
|
||||||
"node": ">=14.0"
|
"node": ">=14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unenv": {
|
"node_modules/unenv": {
|
||||||
"name": "unenv-nightly",
|
"name": "unenv-nightly",
|
||||||
"version": "2.0.0-20241204-140205-a5d5190",
|
"version": "2.0.0-20241204-140205-a5d5190",
|
||||||
|
|
@ -8191,6 +8561,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|
@ -8388,6 +8767,15 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/web-streams-polyfill": {
|
||||||
|
"version": "4.0.0-beta.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
||||||
|
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/web-vitals": {
|
"node_modules/web-vitals": {
|
||||||
"version": "0.2.4",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz",
|
||||||
|
|
@ -9179,6 +9567,15 @@
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod-to-json-schema": {
|
||||||
|
"version": "3.24.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz",
|
||||||
|
"integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.24.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"author": "Jeff Emmett",
|
"author": "Jeff Emmett",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@anthropic-ai/sdk": "^0.33.1",
|
||||||
"@daily-co/daily-js": "^0.60.0",
|
"@daily-co/daily-js": "^0.60.0",
|
||||||
"@daily-co/daily-react": "^0.20.0",
|
"@daily-co/daily-react": "^0.20.0",
|
||||||
"@tldraw/assets": "^3.6.0",
|
"@tldraw/assets": "^3.6.0",
|
||||||
|
|
@ -25,6 +26,7 @@
|
||||||
"@types/markdown-it": "^14.1.1",
|
"@types/markdown-it": "^14.1.1",
|
||||||
"@types/marked": "^5.0.2",
|
"@types/marked": "^5.0.2",
|
||||||
"@vercel/analytics": "^1.2.2",
|
"@vercel/analytics": "^1.2.2",
|
||||||
|
"ai": "^4.1.0",
|
||||||
"cherry-markdown": "^0.8.57",
|
"cherry-markdown": "^0.8.57",
|
||||||
"cloudflare-workers-unfurl": "^0.0.7",
|
"cloudflare-workers-unfurl": "^0.0.7",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
|
|
@ -34,6 +36,7 @@
|
||||||
"jspdf": "^2.5.2",
|
"jspdf": "^2.5.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"marked": "^15.0.4",
|
"marked": "^15.0.4",
|
||||||
|
"openai": "^4.79.3",
|
||||||
"rbush": "^4.0.1",
|
"rbush": "^4.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { atom } from 'tldraw'
|
||||||
|
import { SYSTEM_PROMPT } from '@/prompt'
|
||||||
|
|
||||||
|
export const PROVIDERS = [
|
||||||
|
{
|
||||||
|
id: 'openai',
|
||||||
|
name: 'OpenAI',
|
||||||
|
models: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo'], // 'o1-preview', 'o1-mini'],
|
||||||
|
help: 'https://tldraw.notion.site/Make-Real-Help-93be8b5273d14f7386e14eb142575e6e#a9b75e58b1824962a1a69a2f29ace9be',
|
||||||
|
validate: (key: string) => key.startsWith('sk-'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'anthropic',
|
||||||
|
name: 'Anthropic',
|
||||||
|
models: [
|
||||||
|
'claude-3-5-sonnet-20241022',
|
||||||
|
'claude-3-5-sonnet-20240620',
|
||||||
|
'claude-3-opus-20240229',
|
||||||
|
'claude-3-sonnet-20240229',
|
||||||
|
'claude-3-haiku-20240307',
|
||||||
|
],
|
||||||
|
help: 'https://tldraw.notion.site/Make-Real-Help-93be8b5273d14f7386e14eb142575e6e#3444b55a2ede405286929956d0be6e77',
|
||||||
|
validate: (key: string) => key.startsWith('sk-'),
|
||||||
|
},
|
||||||
|
// { id: 'google', name: 'Google', model: 'Gemeni 1.5 Flash', validate: (key: string) => true },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const makeRealSettings = atom('make real settings', {
|
||||||
|
provider: 'openai' as (typeof PROVIDERS)[number]['id'] | 'all',
|
||||||
|
models: Object.fromEntries(PROVIDERS.map((provider) => [provider.id, provider.models[0]])),
|
||||||
|
keys: {
|
||||||
|
openai: '',
|
||||||
|
anthropic: '',
|
||||||
|
google: '',
|
||||||
|
},
|
||||||
|
prompts: {
|
||||||
|
system: SYSTEM_PROMPT,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export function applySettingsMigrations(settings: any) {
|
||||||
|
const { keys, prompts, ...rest } = settings
|
||||||
|
|
||||||
|
const settingsWithModelsProperty = {
|
||||||
|
provider: 'openai',
|
||||||
|
models: Object.fromEntries(PROVIDERS.map((provider) => [provider.id, provider.models[0]])),
|
||||||
|
keys: {
|
||||||
|
openai: '',
|
||||||
|
anthropic: '',
|
||||||
|
google: '',
|
||||||
|
...keys,
|
||||||
|
},
|
||||||
|
prompts: {
|
||||||
|
system: SYSTEM_PROMPT,
|
||||||
|
...prompts,
|
||||||
|
},
|
||||||
|
...rest,
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingsWithModelsProperty
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
export const SYSTEM_PROMPT = `You are an expert web developer who specializes in building working website prototypes from low-fidelity wireframes. Your job is to accept low-fidelity designs and turn them into high-fidelity interactive and responsive working prototypes. When sent new designs, you should reply with a high-fidelity working prototype as a single HTML file.
|
||||||
|
|
||||||
|
- Use tailwind (via \`cdn.tailwindcss.com\`) for styling.
|
||||||
|
- Put any JavaScript in a script tag with \`type="module"\`.
|
||||||
|
- Use unpkg or skypack to import any required JavaScript dependencies.
|
||||||
|
- Use Google fonts to pull in any open source fonts you require.
|
||||||
|
- If you have any images, load them from Unsplash or use solid colored rectangles as placeholders.
|
||||||
|
- Create SVGs as needed for any icons.
|
||||||
|
|
||||||
|
The designs may include flow charts, diagrams, labels, arrows, sticky notes, screenshots of other applications, or even previous designs. Treat all of these as references for your prototype.
|
||||||
|
|
||||||
|
The designs may include structural elements (such as boxes that represent buttons or content) as well as annotations or figures that describe interactions, behavior, or appearance. Use your best judgement to determine what is an annotation and what should be included in the final result. Annotations are commonly made in the color red. Do NOT include any of those annotations in your final result.
|
||||||
|
|
||||||
|
If there are any questions or underspecified features, use what you know about applications, user experience, and website design patterns to "fill in the blanks". If you're unsure of how the designs should work, take a guess—it's better for you to get it wrong than to leave things incomplete.
|
||||||
|
|
||||||
|
Your prototype should look and feel much more complete and advanced than the wireframes provided. Flesh it out, make it real!
|
||||||
|
|
||||||
|
Remember: you love your designers and want them to be happy. The more complete and impressive your prototype, the happier they will be. You are evaluated on 1) whether your prototype resembles the designs, 2) whether your prototype is interactive and responsive, and 3) whether your prototype is complete and impressive.`
|
||||||
|
|
||||||
|
export const USER_PROMPT =
|
||||||
|
'Here are the latest wireframes. Please reply with a high-fidelity working prototype as a single HTML file.'
|
||||||
|
|
||||||
|
export const USER_PROMPT_WITH_PREVIOUS_DESIGN =
|
||||||
|
"Here are the latest wireframes. There are also some previous outputs here. We have run their code through an 'HTML to screenshot' library to generate a screenshot of the page. The generated screenshot may have some inaccuracies so please use your knowledge of HTML and web development to figure out what any annotations are referring to, which may be different to what is visible in the generated screenshot. Make a new high-fidelity prototype based on your previous work and any new designs or annotations. Again, you should reply with a high-fidelity working prototype as a single HTML file."
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useSync } from "@tldraw/sync"
|
import { useSync } from "@tldraw/sync"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import { Tldraw, Editor } from "tldraw"
|
import { Tldraw, Editor, useTools, useIsToolSelected, DefaultToolbar, TldrawUiMenuItem, DefaultToolbarContent, DefaultKeyboardShortcutsDialog, DefaultKeyboardShortcutsDialogContent, defaultTools } from "tldraw"
|
||||||
import { useParams } from "react-router-dom"
|
import { useParams } from "react-router-dom"
|
||||||
import { ChatBoxTool } from "@/tools/ChatBoxTool"
|
import { ChatBoxTool } from "@/tools/ChatBoxTool"
|
||||||
import { ChatBoxShape } from "@/shapes/ChatBoxShapeUtil"
|
import { ChatBoxShape } from "@/shapes/ChatBoxShapeUtil"
|
||||||
|
|
@ -20,25 +20,122 @@ import { handleInitialPageLoad } from "@/utils/handleInitialPageLoad"
|
||||||
import { MycrozineTemplateTool } from "@/tools/MycrozineTemplateTool"
|
import { MycrozineTemplateTool } from "@/tools/MycrozineTemplateTool"
|
||||||
import { MycrozineTemplateShape } from "@/shapes/MycrozineTemplateShapeUtil"
|
import { MycrozineTemplateShape } from "@/shapes/MycrozineTemplateShapeUtil"
|
||||||
import { registerPropagators, ChangePropagator, TickPropagator, ClickPropagator } from "@/propagators/ScopedPropagators"
|
import { registerPropagators, ChangePropagator, TickPropagator, ClickPropagator } from "@/propagators/ScopedPropagators"
|
||||||
|
import { SlideShapeTool } from "@/tools/SlideShapeTool"
|
||||||
|
import { ISlideShape, SlideShapeUtil } from "@/shapes/SlideShapeUtil"
|
||||||
|
import { SlidesPanel } from "@/slides/SlidesPanel"
|
||||||
|
import { moveToSlide } from "@/slides/useSlides"
|
||||||
|
|
||||||
// Default to production URL if env var isn't available
|
// Default to production URL if env var isn't available
|
||||||
export const WORKER_URL = "https://jeffemmett-canvas.jeffemmett.workers.dev"
|
export const WORKER_URL = "https://jeffemmett-canvas.jeffemmett.workers.dev"
|
||||||
|
|
||||||
const shapeUtils = [
|
const updatedComponents = {
|
||||||
|
...components,
|
||||||
|
HelperButtons: SlidesPanel,
|
||||||
|
Minimap: null,
|
||||||
|
Toolbar: (props: any) => {
|
||||||
|
const tools = useTools()
|
||||||
|
const slideTool = tools['Slide']
|
||||||
|
const isSlideSelected = slideTool ? useIsToolSelected(slideTool) : false
|
||||||
|
return (
|
||||||
|
<DefaultToolbar {...props}>
|
||||||
|
{slideTool && <TldrawUiMenuItem {...slideTool} isSelected={isSlideSelected} />}
|
||||||
|
<DefaultToolbarContent />
|
||||||
|
</DefaultToolbar>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
KeyboardShortcutsDialog: (props: any) => {
|
||||||
|
const tools = useTools()
|
||||||
|
return (
|
||||||
|
<DefaultKeyboardShortcutsDialog {...props}>
|
||||||
|
<TldrawUiMenuItem {...tools['Slide']} />
|
||||||
|
<DefaultKeyboardShortcutsDialogContent />
|
||||||
|
</DefaultKeyboardShortcutsDialog>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const customShapeUtils = [
|
||||||
ChatBoxShape,
|
ChatBoxShape,
|
||||||
VideoChatShape,
|
VideoChatShape,
|
||||||
EmbedShape,
|
EmbedShape,
|
||||||
// MycrozineTemplateShape,
|
SlideShapeUtil,
|
||||||
// MarkdownShape
|
MycrozineTemplateShape,
|
||||||
|
MarkdownShape
|
||||||
]
|
]
|
||||||
const tools = [
|
const customTools = [
|
||||||
ChatBoxTool,
|
ChatBoxTool,
|
||||||
VideoChatTool,
|
VideoChatTool,
|
||||||
EmbedTool,
|
EmbedTool,
|
||||||
// MycrozineTemplateTool,
|
SlideShapeTool,
|
||||||
// MarkdownTool
|
MycrozineTemplateTool,
|
||||||
|
MarkdownTool
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const updatedOverrides = {
|
||||||
|
...overrides,
|
||||||
|
actions(editor: Editor, actions: any) {
|
||||||
|
return {
|
||||||
|
...actions,
|
||||||
|
'next-slide': {
|
||||||
|
id: 'next-slide',
|
||||||
|
label: 'Next slide',
|
||||||
|
kbd: 'right',
|
||||||
|
onSelect() {
|
||||||
|
const slides = editor.getCurrentPageShapes().filter(shape => shape.type === 'Slide')
|
||||||
|
if (slides.length === 0) return
|
||||||
|
|
||||||
|
const currentSlide = editor.getSelectedShapes().find(shape => shape.type === 'Slide')
|
||||||
|
const currentIndex = currentSlide
|
||||||
|
? slides.findIndex(slide => slide.id === currentSlide.id)
|
||||||
|
: -1
|
||||||
|
|
||||||
|
console.log('Current index:', currentIndex)
|
||||||
|
console.log('Current slide:', currentSlide)
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate next index with wraparound
|
||||||
|
const nextIndex = currentIndex === -1
|
||||||
|
? 0
|
||||||
|
: currentIndex >= slides.length - 1
|
||||||
|
? 0
|
||||||
|
: currentIndex + 1
|
||||||
|
|
||||||
|
const nextSlide = slides[nextIndex]
|
||||||
|
|
||||||
|
editor.select(nextSlide.id)
|
||||||
|
editor.stopCameraAnimation()
|
||||||
|
moveToSlide(editor, nextSlide as ISlideShape)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'previous-slide': {
|
||||||
|
id: 'previous-slide',
|
||||||
|
label: 'Previous slide',
|
||||||
|
kbd: 'left',
|
||||||
|
onSelect() {
|
||||||
|
const slides = editor.getCurrentPageShapes().filter(shape => shape.type === 'Slide')
|
||||||
|
if (slides.length === 0) return
|
||||||
|
|
||||||
|
const currentSlide = editor.getSelectedShapes().find(shape => shape.type === 'Slide')
|
||||||
|
const currentIndex = currentSlide
|
||||||
|
? slides.findIndex(slide => slide.id === currentSlide.id)
|
||||||
|
: -1
|
||||||
|
|
||||||
|
// Calculate previous index with wraparound
|
||||||
|
const previousIndex = currentIndex <= 0
|
||||||
|
? slides.length - 1
|
||||||
|
: currentIndex - 1
|
||||||
|
|
||||||
|
const previousSlide = slides[previousIndex]
|
||||||
|
|
||||||
|
editor.select(previousSlide.id)
|
||||||
|
editor.stopCameraAnimation()
|
||||||
|
moveToSlide(editor, previousSlide as ISlideShape)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export function Board() {
|
export function Board() {
|
||||||
const { slug } = useParams<{ slug: string }>()
|
const { slug } = useParams<{ slug: string }>()
|
||||||
const roomId = slug || "default-room"
|
const roomId = slug || "default-room"
|
||||||
|
|
@ -47,7 +144,7 @@ export function Board() {
|
||||||
() => ({
|
() => ({
|
||||||
uri: `${WORKER_URL}/connect/${roomId}`,
|
uri: `${WORKER_URL}/connect/${roomId}`,
|
||||||
assets: multiplayerAssetStore,
|
assets: multiplayerAssetStore,
|
||||||
shapeUtils: [...shapeUtils, ...defaultShapeUtils],
|
shapeUtils: [...defaultShapeUtils, ...customShapeUtils],
|
||||||
bindingUtils: [...defaultBindingUtils],
|
bindingUtils: [...defaultBindingUtils],
|
||||||
}),
|
}),
|
||||||
[roomId],
|
[roomId],
|
||||||
|
|
@ -56,14 +153,17 @@ export function Board() {
|
||||||
const store = useSync(storeConfig)
|
const store = useSync(storeConfig)
|
||||||
const [editor, setEditor] = useState<Editor | null>(null)
|
const [editor, setEditor] = useState<Editor | null>(null)
|
||||||
|
|
||||||
|
//console.log("store:", store)
|
||||||
|
//console.log("store.store:",store.store)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: "fixed", inset: 0 }}>
|
<div style={{ position: "fixed", inset: 0 }}>
|
||||||
<Tldraw
|
<Tldraw
|
||||||
store={store.store}
|
store={store.store}
|
||||||
shapeUtils={shapeUtils}
|
shapeUtils={customShapeUtils}
|
||||||
tools={tools}
|
tools={customTools}
|
||||||
components={components}
|
components={updatedComponents}
|
||||||
overrides={overrides}
|
overrides={updatedOverrides}
|
||||||
cameraOptions={{
|
cameraOptions={{
|
||||||
zoomSteps: [
|
zoomSteps: [
|
||||||
0.001, // Min zoom
|
0.001, // Min zoom
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import {
|
||||||
|
BaseBoxShapeUtil,
|
||||||
|
Geometry2d,
|
||||||
|
RecordProps,
|
||||||
|
Rectangle2d,
|
||||||
|
SVGContainer,
|
||||||
|
ShapeUtil,
|
||||||
|
T,
|
||||||
|
TLBaseShape,
|
||||||
|
getPerfectDashProps,
|
||||||
|
resizeBox,
|
||||||
|
useValue,
|
||||||
|
} from 'tldraw'
|
||||||
|
import { moveToSlide, useSlides } from '@/slides/useSlides'
|
||||||
|
|
||||||
|
export type ISlideShape = TLBaseShape<
|
||||||
|
'Slide',
|
||||||
|
{
|
||||||
|
w: number
|
||||||
|
h: number
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
export class SlideShapeUtil extends BaseBoxShapeUtil<ISlideShape> {
|
||||||
|
static override type = "Slide"
|
||||||
|
|
||||||
|
// static override props = {
|
||||||
|
// w: T.number,
|
||||||
|
// h: T.number,
|
||||||
|
// }
|
||||||
|
|
||||||
|
override canBind = () => false
|
||||||
|
override hideRotateHandle = () => true
|
||||||
|
|
||||||
|
getDefaultProps(): ISlideShape["props"] {
|
||||||
|
return {
|
||||||
|
w: 720,
|
||||||
|
h: 480,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getGeometry(shape: ISlideShape): Geometry2d {
|
||||||
|
return new Rectangle2d({
|
||||||
|
width: shape.props.w,
|
||||||
|
height: shape.props.h,
|
||||||
|
isFilled: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override onRotate = (initial: ISlideShape) => initial
|
||||||
|
override onResize(shape: ISlideShape, info: any) {
|
||||||
|
return resizeBox(shape, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
override onDoubleClick = (shape: ISlideShape) => {
|
||||||
|
moveToSlide(this.editor, shape)
|
||||||
|
this.editor.selectNone()
|
||||||
|
}
|
||||||
|
|
||||||
|
override onDoubleClickEdge = (shape: ISlideShape) => {
|
||||||
|
moveToSlide(this.editor, shape)
|
||||||
|
this.editor.selectNone()
|
||||||
|
}
|
||||||
|
|
||||||
|
component(shape: ISlideShape) {
|
||||||
|
const bounds = this.editor.getShapeGeometry(shape).bounds
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const zoomLevel = useValue('zoom level', () => this.editor.getZoomLevel(), [this.editor])
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const slides = useSlides()
|
||||||
|
const index = slides.findIndex((s) => s.id === shape.id)
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const handleLabelPointerDown = useCallback(() => this.editor.select(shape.id), [shape.id])
|
||||||
|
|
||||||
|
if (!bounds) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div onPointerDown={handleLabelPointerDown} className="slide-shape-label">
|
||||||
|
{`Slide ${index + 1}`}
|
||||||
|
</div>
|
||||||
|
<SVGContainer>
|
||||||
|
<g
|
||||||
|
style={{
|
||||||
|
stroke: 'var(--color-text)',
|
||||||
|
strokeWidth: 'calc(1px * var(--tl-scale))',
|
||||||
|
opacity: 0.25,
|
||||||
|
}}
|
||||||
|
pointerEvents="none"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
{bounds.sides.map((side, i) => {
|
||||||
|
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
|
||||||
|
side[0].dist(side[1]),
|
||||||
|
1 / zoomLevel,
|
||||||
|
{
|
||||||
|
style: 'dashed',
|
||||||
|
lengthRatio: 6,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<line
|
||||||
|
key={i}
|
||||||
|
x1={side[0].x}
|
||||||
|
y1={side[0].y}
|
||||||
|
x2={side[1].x}
|
||||||
|
y2={side[1].y}
|
||||||
|
strokeDasharray={strokeDasharray}
|
||||||
|
strokeDashoffset={strokeDashoffset}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</g>
|
||||||
|
</SVGContainer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
indicator(shape: ISlideShape) {
|
||||||
|
return <rect width={shape.props.w} height={shape.props.h} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { TldrawUiButton, stopEventPropagation, track, useEditor, useValue } from 'tldraw'
|
||||||
|
import { moveToSlide, useCurrentSlide, useSlides } from '@/slides/useSlides'
|
||||||
|
|
||||||
|
export const SlidesPanel = track(() => {
|
||||||
|
const editor = useEditor()
|
||||||
|
const slides = useSlides()
|
||||||
|
const currentSlide = useCurrentSlide()
|
||||||
|
const selectedShapes = useValue('selected shapes', () => editor.getSelectedShapes(), [editor])
|
||||||
|
|
||||||
|
if (slides.length === 0) return null
|
||||||
|
return (
|
||||||
|
<div className="slides-panel scroll-light" onPointerDown={(e) => stopEventPropagation(e)}>
|
||||||
|
{slides.map((slide, i) => {
|
||||||
|
const isSelected = selectedShapes.includes(slide)
|
||||||
|
return (
|
||||||
|
<TldrawUiButton
|
||||||
|
key={'slides-panel-button:' + slide.id}
|
||||||
|
type="normal"
|
||||||
|
className="slides-panel-button"
|
||||||
|
onClick={() => {
|
||||||
|
moveToSlide(editor, slide)
|
||||||
|
// Switch to select tool and select the slide shape
|
||||||
|
editor.setCurrentTool('select')
|
||||||
|
editor.select(slide)
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
background: currentSlide?.id === slide.id ? 'var(--color-background)' : 'transparent',
|
||||||
|
outline: isSelected ? 'var(--color-selection-stroke) solid 1.5px' : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`Slide ${i + 1}`}
|
||||||
|
</TldrawUiButton>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
.slides-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
max-height: calc(100% - 110px);
|
||||||
|
margin: 50px 0px;
|
||||||
|
padding: 4px;
|
||||||
|
background-color: var(--color-low);
|
||||||
|
pointer-events: all;
|
||||||
|
border-top-right-radius: var(--radius-4);
|
||||||
|
border-bottom-right-radius: var(--radius-4);
|
||||||
|
overflow: auto;
|
||||||
|
border-right: 2px solid var(--color-background);
|
||||||
|
border-bottom: 2px solid var(--color-background);
|
||||||
|
border-top: 2px solid var(--color-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slides-panel-button {
|
||||||
|
border-radius: var(--radius-4);
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-shape-label {
|
||||||
|
pointer-events: all;
|
||||||
|
position: absolute;
|
||||||
|
background: var(--color-low);
|
||||||
|
padding: calc(12px * var(--tl-scale));
|
||||||
|
border-bottom-right-radius: calc(var(--radius-4) * var(--tl-scale));
|
||||||
|
font-size: calc(12px * var(--tl-scale));
|
||||||
|
color: var(--color-text);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { EASINGS, Editor, atom, useEditor, useValue } from 'tldraw'
|
||||||
|
import { ISlideShape } from '@/shapes/SlideShapeUtil'
|
||||||
|
|
||||||
|
export const $currentSlide = atom<ISlideShape | null>('current slide', null)
|
||||||
|
|
||||||
|
export function moveToSlide(editor: Editor, slide: ISlideShape) {
|
||||||
|
const bounds = editor.getShapePageBounds(slide.id)
|
||||||
|
if (!bounds) return
|
||||||
|
$currentSlide.set(slide)
|
||||||
|
editor.selectNone()
|
||||||
|
editor.zoomToBounds(bounds, {
|
||||||
|
animation: { duration: 500, easing: EASINGS.easeInOutCubic },
|
||||||
|
inset: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSlides() {
|
||||||
|
const editor = useEditor()
|
||||||
|
return useValue<ISlideShape[]>('slide shapes', () => getSlides(editor), [editor])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCurrentSlide() {
|
||||||
|
return useValue($currentSlide)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlides(editor: Editor) {
|
||||||
|
return editor
|
||||||
|
.getSortedChildIdsForParent(editor.getCurrentPageId())
|
||||||
|
.map((id) => editor.getShape(id))
|
||||||
|
.filter((s) => s?.type === 'Slide') as ISlideShape[]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { BaseBoxShapeTool } from 'tldraw'
|
||||||
|
|
||||||
|
export class SlideShapeTool extends BaseBoxShapeTool {
|
||||||
|
static override id = 'Slide'
|
||||||
|
static override initial = 'idle'
|
||||||
|
override shapeType = 'Slide'
|
||||||
|
|
||||||
|
constructor(editor: any) {
|
||||||
|
super(editor)
|
||||||
|
console.log('SlideShapeTool constructed', { id: this.id, shapeType: this.shapeType })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -102,7 +102,7 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
|
||||||
{/* Creation Tools Group */}
|
{/* Creation Tools Group */}
|
||||||
<TldrawUiMenuGroup id="creation-tools">
|
<TldrawUiMenuGroup id="creation-tools">
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
id="video-chat"
|
id="VideoChat"
|
||||||
label="Create Video Chat"
|
label="Create Video Chat"
|
||||||
icon="video"
|
icon="video"
|
||||||
kbd="alt+v"
|
kbd="alt+v"
|
||||||
|
|
@ -112,7 +112,7 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
id="chat-box"
|
id="ChatBox"
|
||||||
label="Create Chat Box"
|
label="Create Chat Box"
|
||||||
icon="chat"
|
icon="chat"
|
||||||
kbd="alt+c"
|
kbd="alt+c"
|
||||||
|
|
@ -122,7 +122,7 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
id="embed"
|
id="Embed"
|
||||||
label="Create Embed"
|
label="Create Embed"
|
||||||
icon="embed"
|
icon="embed"
|
||||||
kbd="alt+e"
|
kbd="alt+e"
|
||||||
|
|
@ -131,7 +131,16 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
|
||||||
editor.setCurrentTool("Embed")
|
editor.setCurrentTool("Embed")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/*
|
<TldrawUiMenuItem
|
||||||
|
id="Slide"
|
||||||
|
label="Create Slide"
|
||||||
|
icon="slides"
|
||||||
|
kbd="alt+s"
|
||||||
|
disabled={hasSelection}
|
||||||
|
onSelect={() => {
|
||||||
|
editor.setCurrentTool("Slide")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
id="mycrozine-template"
|
id="mycrozine-template"
|
||||||
label="Create Mycrozine Template"
|
label="Create Mycrozine Template"
|
||||||
|
|
@ -151,8 +160,7 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
editor.setCurrentTool("Markdown")
|
editor.setCurrentTool("Markdown")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
*/}
|
|
||||||
</TldrawUiMenuGroup>
|
</TldrawUiMenuGroup>
|
||||||
|
|
||||||
{/* Frame Controls */}
|
{/* Frame Controls */}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,14 @@ export function CustomToolbar() {
|
||||||
isSelected={tools["Embed"].id === editor.getCurrentToolId()}
|
isSelected={tools["Embed"].id === editor.getCurrentToolId()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{tools["SlideShape"] && (
|
||||||
|
<TldrawUiMenuItem
|
||||||
|
{...tools["SlideShape"]}
|
||||||
|
icon="slides"
|
||||||
|
label="Slide"
|
||||||
|
isSelected={tools["SlideShape"].id === editor.getCurrentToolId()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/*
|
{/*
|
||||||
{tools["Markdown"] && (
|
{tools["Markdown"] && (
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
import {
|
||||||
|
TLUiDialogProps,
|
||||||
|
TldrawUiButton,
|
||||||
|
TldrawUiButtonLabel,
|
||||||
|
TldrawUiDialogBody,
|
||||||
|
TldrawUiDialogCloseButton,
|
||||||
|
TldrawUiDialogFooter,
|
||||||
|
TldrawUiDialogHeader,
|
||||||
|
TldrawUiDialogTitle,
|
||||||
|
TldrawUiIcon,
|
||||||
|
TldrawUiInput,
|
||||||
|
useReactor,
|
||||||
|
useValue,
|
||||||
|
} from 'tldraw'
|
||||||
|
import { PROVIDERS, makeRealSettings } from '../lib/settings'
|
||||||
|
import { SYSTEM_PROMPT } from '@/prompt'
|
||||||
|
|
||||||
|
export function SettingsDialog({ onClose }: TLUiDialogProps) {
|
||||||
|
const settings = useValue('settings', () => makeRealSettings.get(), [])
|
||||||
|
|
||||||
|
useReactor(
|
||||||
|
'update settings local storage',
|
||||||
|
() => {
|
||||||
|
localStorage.setItem('makereal_settings_2', JSON.stringify(makeRealSettings.get()))
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TldrawUiDialogHeader>
|
||||||
|
<TldrawUiDialogTitle>Settings</TldrawUiDialogTitle>
|
||||||
|
<TldrawUiDialogCloseButton />
|
||||||
|
</TldrawUiDialogHeader>
|
||||||
|
<TldrawUiDialogBody
|
||||||
|
style={{ maxWidth: 350, display: 'flex', flexDirection: 'column', gap: 8 }}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
To use Make Real, enter your API key for each model provider that you wish to use. Draw
|
||||||
|
some shapes, then select the shapes and click Make Real.{' '}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://tldraw.notion.site/Make-Real-FAQs-93be8b5273d14f7386e14eb142575e6e?pvs=4"
|
||||||
|
>
|
||||||
|
<u>Read our guide.</u>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginTop: 8 }}>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', gap: 4 }}>
|
||||||
|
<label style={{ flexGrow: 2 }}>Provider</label>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
className="apikey_select"
|
||||||
|
value={settings.provider}
|
||||||
|
onChange={(e) => {
|
||||||
|
makeRealSettings.update((s) => ({ ...s, provider: e.target.value as any }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{PROVIDERS.map((provider) => {
|
||||||
|
return (
|
||||||
|
<option key={provider.id + 'option'} value={provider.id}>
|
||||||
|
{provider.name}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<option value="all">All</option>
|
||||||
|
</select>
|
||||||
|
{settings.provider !== 'all' && (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', gap: 4 }}>
|
||||||
|
<label style={{ flexGrow: 2 }}>Model</label>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
className="apikey_select"
|
||||||
|
value={settings.models[settings.provider]}
|
||||||
|
onChange={(e) => {
|
||||||
|
makeRealSettings.update((s) => ({
|
||||||
|
...s,
|
||||||
|
models: { ...s.models, [settings.provider]: e.target.value as any },
|
||||||
|
}))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{PROVIDERS.find((p) => p.id === settings.provider)!.models.map((model) => {
|
||||||
|
return (
|
||||||
|
<option key={model + 'option'} value={model}>
|
||||||
|
{model}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<hr style={{ margin: '12px 0px' }} />
|
||||||
|
{PROVIDERS.map((provider) => {
|
||||||
|
if (provider.id === 'google') return null
|
||||||
|
const value = settings.keys[provider.id as keyof typeof settings.keys]
|
||||||
|
return (
|
||||||
|
<ApiKeyInput
|
||||||
|
provider={provider}
|
||||||
|
key={provider.name + 'key'}
|
||||||
|
value={value}
|
||||||
|
warning={
|
||||||
|
value === '' && (settings.provider === provider.id || settings.provider === 'all')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
{/* <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', gap: 4 }}>
|
||||||
|
<label style={{ flexGrow: 2 }}>Google</label>
|
||||||
|
</div>
|
||||||
|
<TldrawUiInput
|
||||||
|
className="apikey_input"
|
||||||
|
value={settings.keys.google}
|
||||||
|
placeholder="risky but cool"
|
||||||
|
onValueChange={(value) => {
|
||||||
|
const next = { ...settings, keys: { ...settings.keys, google: value } }
|
||||||
|
localStorage.setItem('makereal_settings_2', JSON.stringify(next))
|
||||||
|
makeRealSettings.set(next)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
<hr style={{ margin: '12px 0px' }} />
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', gap: 4 }}>
|
||||||
|
<label style={{ flexGrow: 2 }}>System prompt</label>
|
||||||
|
<button
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
makeRealSettings.update((s) => ({
|
||||||
|
...s,
|
||||||
|
prompts: { ...s.prompts, system: SYSTEM_PROMPT },
|
||||||
|
}))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<TldrawUiInput
|
||||||
|
className="apikey_input"
|
||||||
|
value={settings.prompts.system}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
makeRealSettings.update((s) => ({ ...s, prompts: { ...s.prompts, system: value } }))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TldrawUiDialogBody>
|
||||||
|
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||||
|
<TldrawUiButton
|
||||||
|
type="primary"
|
||||||
|
onClick={async () => {
|
||||||
|
onClose()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TldrawUiButtonLabel>Save</TldrawUiButtonLabel>
|
||||||
|
</TldrawUiButton>
|
||||||
|
</TldrawUiDialogFooter>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApiKeyInput({
|
||||||
|
provider,
|
||||||
|
value,
|
||||||
|
warning,
|
||||||
|
}: {
|
||||||
|
provider: (typeof PROVIDERS)[number]
|
||||||
|
value: string
|
||||||
|
warning: boolean
|
||||||
|
}) {
|
||||||
|
const isValid = value.length === 0 || provider.validate(value)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', gap: 4, alignItems: 'center' }}>
|
||||||
|
<label style={{ flexGrow: 2, color: warning ? 'red' : 'var(--color-text)' }}>
|
||||||
|
{provider.name} API key
|
||||||
|
</label>
|
||||||
|
<a style={{ cursor: 'pointer', pointerEvents: 'all' }} target="_blank" href={provider.help}>
|
||||||
|
<TldrawUiIcon
|
||||||
|
className="apikey_help_icon"
|
||||||
|
small
|
||||||
|
icon={provider.validate(value) ? 'check' : 'question-mark-circle'}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<TldrawUiInput
|
||||||
|
className={`apikey_input ${isValid ? '' : 'apikey_input__invalid'}`}
|
||||||
|
value={value}
|
||||||
|
placeholder="risky but cool"
|
||||||
|
onValueChange={(value) => {
|
||||||
|
makeRealSettings.update((s) => ({ ...s, keys: { ...s.keys, [provider.id]: value } }))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { TLUiOverrides } from "tldraw"
|
import { shapeIdValidator, TLUiOverrides } from "tldraw"
|
||||||
import {
|
import {
|
||||||
cameraHistory,
|
cameraHistory,
|
||||||
copyLinkToCurrentView,
|
copyLinkToCurrentView,
|
||||||
|
|
@ -68,6 +68,7 @@ export const overrides: TLUiOverrides = {
|
||||||
label: "Video Chat",
|
label: "Video Chat",
|
||||||
kbd: "alt+v",
|
kbd: "alt+v",
|
||||||
readonlyOk: true,
|
readonlyOk: true,
|
||||||
|
type: "VideoChat",
|
||||||
onSelect: () => editor.setCurrentTool("VideoChat"),
|
onSelect: () => editor.setCurrentTool("VideoChat"),
|
||||||
},
|
},
|
||||||
ChatBox: {
|
ChatBox: {
|
||||||
|
|
@ -76,6 +77,7 @@ export const overrides: TLUiOverrides = {
|
||||||
label: "Chat",
|
label: "Chat",
|
||||||
kbd: "alt+c",
|
kbd: "alt+c",
|
||||||
readonlyOk: true,
|
readonlyOk: true,
|
||||||
|
type: "ChatBox",
|
||||||
onSelect: () => editor.setCurrentTool("ChatBox"),
|
onSelect: () => editor.setCurrentTool("ChatBox"),
|
||||||
},
|
},
|
||||||
Embed: {
|
Embed: {
|
||||||
|
|
@ -84,26 +86,41 @@ export const overrides: TLUiOverrides = {
|
||||||
label: "Embed",
|
label: "Embed",
|
||||||
kbd: "alt+e",
|
kbd: "alt+e",
|
||||||
readonlyOk: true,
|
readonlyOk: true,
|
||||||
|
type: "Embed",
|
||||||
onSelect: () => editor.setCurrentTool("Embed"),
|
onSelect: () => editor.setCurrentTool("Embed"),
|
||||||
},
|
},
|
||||||
/*
|
SlideShape: {
|
||||||
|
id: "Slide",
|
||||||
|
icon: "slides",
|
||||||
|
label: "Slide",
|
||||||
|
kbd: "alt+s",
|
||||||
|
type: "Slide",
|
||||||
|
readonlyOk: true,
|
||||||
|
onSelect: () => {
|
||||||
|
console.log('SlideShape tool selected from menu')
|
||||||
|
console.log('Current tool before:', editor.getCurrentToolId())
|
||||||
|
editor.setCurrentTool("Slide")
|
||||||
|
console.log('Current tool after:', editor.getCurrentToolId())
|
||||||
|
},
|
||||||
|
},
|
||||||
Markdown: {
|
Markdown: {
|
||||||
id: "Markdown",
|
id: "Markdown",
|
||||||
icon: "markdown",
|
icon: "markdown",
|
||||||
label: "Markdown",
|
label: "Markdown",
|
||||||
kbd: "alt+m",
|
kbd: "alt+m",
|
||||||
readonlyOk: true,
|
readonlyOk: true,
|
||||||
|
type: "Markdown",
|
||||||
onSelect: () => editor.setCurrentTool("Markdown"),
|
onSelect: () => editor.setCurrentTool("Markdown"),
|
||||||
},
|
},
|
||||||
MycrozineTemplate: {
|
MycrozineTemplate: {
|
||||||
id: "MycrozineTemplate",
|
id: "MycrozineTemplate",
|
||||||
icon: "rectangle",
|
icon: "rectangle",
|
||||||
label: "Mycrozine Template",
|
label: "Mycrozine Template",
|
||||||
|
type: "MycrozineTemplate",
|
||||||
kbd: "m",
|
kbd: "m",
|
||||||
readonlyOk: true,
|
readonlyOk: true,
|
||||||
onSelect: () => editor.setCurrentTool("MycrozineTemplate"),
|
onSelect: () => editor.setCurrentTool("MycrozineTemplate"),
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
hand: {
|
hand: {
|
||||||
...tools.hand,
|
...tools.hand,
|
||||||
onDoubleClick: (info: any) => {
|
onDoubleClick: (info: any) => {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
createTLSchema,
|
createTLSchema,
|
||||||
defaultBindingSchemas,
|
defaultBindingSchemas,
|
||||||
defaultShapeSchemas,
|
defaultShapeSchemas,
|
||||||
|
shapeIdValidator,
|
||||||
} from "@tldraw/tlschema"
|
} from "@tldraw/tlschema"
|
||||||
import { AutoRouter, IRequest, error } from "itty-router"
|
import { AutoRouter, IRequest, error } from "itty-router"
|
||||||
import throttle from "lodash.throttle"
|
import throttle from "lodash.throttle"
|
||||||
|
|
@ -16,6 +17,8 @@ import { VideoChatShape } from "@/shapes/VideoChatShapeUtil"
|
||||||
import { EmbedShape } from "@/shapes/EmbedShapeUtil"
|
import { EmbedShape } from "@/shapes/EmbedShapeUtil"
|
||||||
import { MarkdownShape } from "@/shapes/MarkdownShapeUtil"
|
import { MarkdownShape } from "@/shapes/MarkdownShapeUtil"
|
||||||
import { MycrozineTemplateShape } from "@/shapes/MycrozineTemplateShapeUtil"
|
import { MycrozineTemplateShape } from "@/shapes/MycrozineTemplateShapeUtil"
|
||||||
|
import { T } from "@tldraw/tldraw"
|
||||||
|
import { SlideShapeUtil } from "@/shapes/SlideShapeUtil"
|
||||||
|
|
||||||
// add custom shapes and bindings here if needed:
|
// add custom shapes and bindings here if needed:
|
||||||
export const customSchema = createTLSchema({
|
export const customSchema = createTLSchema({
|
||||||
|
|
@ -41,6 +44,10 @@ export const customSchema = createTLSchema({
|
||||||
props: MycrozineTemplateShape.props,
|
props: MycrozineTemplateShape.props,
|
||||||
migrations: MycrozineTemplateShape.migrations,
|
migrations: MycrozineTemplateShape.migrations,
|
||||||
},
|
},
|
||||||
|
Slide: {
|
||||||
|
props: SlideShapeUtil.props,
|
||||||
|
migrations: SlideShapeUtil.migrations,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
bindings: defaultBindingSchemas,
|
bindings: defaultBindingSchemas,
|
||||||
})
|
})
|
||||||
|
|
@ -206,7 +213,7 @@ export class TldrawDurableObject {
|
||||||
initialSnapshot.documents = initialSnapshot.documents.filter(
|
initialSnapshot.documents = initialSnapshot.documents.filter(
|
||||||
(record) => {
|
(record) => {
|
||||||
const shape = record.state as TLShape
|
const shape = record.state as TLShape
|
||||||
return shape.type !== "chatBox"
|
return shape.type !== "ChatBox"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue