mirror of
https://github.com/deiucanta/chatpad.git
synced 2026-03-11 09:04:31 +00:00
feat: interop through app actions
This commit is contained in:
parent
09c01e4b38
commit
9f305f3203
18 changed files with 862 additions and 188 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,4 +2,5 @@ node_modules
|
|||
.parcel-cache
|
||||
dist
|
||||
notes.txt
|
||||
lib
|
||||
lib
|
||||
.env
|
||||
|
|
@ -22,3 +22,7 @@ micros:
|
|||
public_routes:
|
||||
- "/public/*"
|
||||
provide_actions: true
|
||||
presets:
|
||||
env:
|
||||
- name: SPACE_ACCESS_TOKEN
|
||||
description: Space Access Token used for optional interoperability features
|
||||
|
|
|
|||
281
backend/package-lock.json
generated
281
backend/package-lock.json
generated
|
|
@ -11,6 +11,8 @@
|
|||
"dependencies": {
|
||||
"deta": "^2.0.0",
|
||||
"deta-space-actions": "^0.1.9",
|
||||
"deta-space-client": "^2.2.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"nanoid": "^3.3.6"
|
||||
},
|
||||
|
|
@ -512,11 +514,18 @@
|
|||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
|
|
@ -745,11 +754,23 @@
|
|||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
|
|
@ -760,8 +781,7 @@
|
|||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
|
|
@ -814,6 +834,14 @@
|
|||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
|
|
@ -888,6 +916,18 @@
|
|||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/deta-space-client": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deta-space-client/-/deta-space-client-2.2.2.tgz",
|
||||
"integrity": "sha512-xGficxfyJBHfQydr3qmNjaadmQfbYPfJWS/6qXM/hGLS8a8OQuJAhaASBPJTW5DXKopRHZAafqNTEu8IGgevyw==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^4.0.0",
|
||||
"yargs": "^17.7.1"
|
||||
},
|
||||
"bin": {
|
||||
"space-client": "dist/cli/index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
|
|
@ -897,6 +937,17 @@
|
|||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
|
|
@ -909,6 +960,11 @@
|
|||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
|
|
@ -941,8 +997,6 @@
|
|||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
|
@ -1169,6 +1223,14 @@
|
|||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
|
|
@ -1316,6 +1378,14 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
|
|
@ -1799,6 +1869,14 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
|
@ -1968,6 +2046,30 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -2338,12 +2440,61 @@
|
|||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
|
|
@ -2814,11 +2965,15 @@
|
|||
"peer": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
|
|
@ -2968,11 +3123,20 @@
|
|||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
|
|
@ -2980,8 +3144,7 @@
|
|||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
|
|
@ -3025,6 +3188,14 @@
|
|||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
|
|
@ -3076,12 +3247,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"deta-space-client": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deta-space-client/-/deta-space-client-2.2.2.tgz",
|
||||
"integrity": "sha512-xGficxfyJBHfQydr3qmNjaadmQfbYPfJWS/6qXM/hGLS8a8OQuJAhaASBPJTW5DXKopRHZAafqNTEu8IGgevyw==",
|
||||
"requires": {
|
||||
"cross-fetch": "^4.0.0",
|
||||
"yargs": "^17.7.1"
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "16.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ=="
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
|
|
@ -3094,6 +3279,11 @@
|
|||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
|
|
@ -3119,9 +3309,7 @@
|
|||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
|
|
@ -3291,6 +3479,11 @@
|
|||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
|
|
@ -3399,6 +3592,11 @@
|
|||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
|
|
@ -3741,6 +3939,11 @@
|
|||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
|
@ -3867,6 +4070,24 @@
|
|||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -4103,12 +4324,46 @@
|
|||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"requires": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"dependencies": {
|
||||
"deta": "^2.0.0",
|
||||
"deta-space-actions": "^0.1.9",
|
||||
"deta-space-client": "^2.2.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"nanoid": "^3.3.6"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { Deta } from 'deta'
|
||||
import { nanoid } from "nanoid"
|
||||
|
||||
export * from '../../src/db/types.js'
|
||||
|
||||
const deta = Deta()
|
||||
|
||||
export const chats = deta.Base('chats')
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import express from 'express'
|
||||
import { CardType, createActions, Inputs } from 'deta-space-actions'
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
import { chats, messages, prompts, settings, generateKey } from './db.js'
|
||||
import type { Chat, Prompt, Settings } from './db.js'
|
||||
import type { Chat, Prompt, Settings } from './types.js'
|
||||
import { createChatWithMessage } from './openai.js'
|
||||
import { useInterop } from './interop.js'
|
||||
|
||||
const app = express()
|
||||
const actions = createActions()
|
||||
const interop = useInterop()
|
||||
|
||||
const domain = process.env.DETA_SPACE_APP_HOSTNAME
|
||||
|
||||
|
|
@ -35,6 +39,36 @@ app.get('/public/chats/:key/messages', async (req, res) => {
|
|||
res.json(items)
|
||||
})
|
||||
|
||||
app.get('/space/actions/config', async (req, res) => {
|
||||
res.json({
|
||||
isSetup: interop.isSetup
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/space/actions', async (req, res) => {
|
||||
const actions = await interop.listActions()
|
||||
|
||||
console.log(actions)
|
||||
|
||||
const filtered = actions.filter(action => {
|
||||
const inputs = action.input
|
||||
if (!inputs || inputs.length < 1) return false
|
||||
|
||||
const textInputs = inputs.filter(input => input.type === 'string')
|
||||
|
||||
return textInputs.length > 0
|
||||
})
|
||||
|
||||
res.json(filtered)
|
||||
})
|
||||
|
||||
app.post('/space/actions/invoke', async (req, res) => {
|
||||
const { instanceId, actionName, payload } = req.body
|
||||
|
||||
const invocationRes = await interop.invokeAction(instanceId, actionName, payload)
|
||||
res.json(invocationRes)
|
||||
})
|
||||
|
||||
actions.add<{ prompt?: string }>({
|
||||
name: 'create_chat',
|
||||
title: 'Create Chat',
|
||||
|
|
|
|||
36
backend/src/interop.ts
Normal file
36
backend/src/interop.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { fetchFn } from "deta-space-client"
|
||||
import { AppAction } from "./types"
|
||||
|
||||
export const useInterop = (token?: string) => {
|
||||
const authToken = token || process.env.SPACE_ACCESS_TOKEN || ''
|
||||
const fetch = fetchFn(authToken)
|
||||
|
||||
const request = async (path: string, body?: any, method = 'GET') => {
|
||||
const res = await fetch(path, {
|
||||
method,
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
return res.json()
|
||||
}
|
||||
|
||||
const listActions = async () => {
|
||||
const data = await request('/actions')
|
||||
return data.actions as AppAction[]
|
||||
}
|
||||
|
||||
const invokeAction = async (instanceId: string, actionName: string, payload?: any) => {
|
||||
const data = await request(`/actions/${instanceId}/${actionName}`, payload, 'POST')
|
||||
return data
|
||||
}
|
||||
|
||||
return {
|
||||
request,
|
||||
listActions,
|
||||
invokeAction,
|
||||
isSetup: !!authToken
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { chats, messages, prompts, generateKey, Prompt, Message, Chat, Settings } from './db.js'
|
||||
import { chats, messages, prompts, generateKey } from './db.js'
|
||||
import type { Prompt, Message, Chat, Settings } from './types.js'
|
||||
|
||||
import { getSystemMessage, createChatCompletion } from '../../src/utils/openai.js'
|
||||
|
||||
|
|
|
|||
1
backend/src/types.ts
Normal file
1
backend/src/types.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from '../../src/db/types.js'
|
||||
23
package-lock.json
generated
23
package-lock.json
generated
|
|
@ -12,6 +12,7 @@
|
|||
"@mantine/hooks": "^6.0.1",
|
||||
"@mantine/next": "^6.0.1",
|
||||
"@mantine/notifications": "^6.0.1",
|
||||
"@mantine/spotlight": "^6.0.2",
|
||||
"@tabler/icons-react": "^2.9.0",
|
||||
"@tanstack/react-location": "^3.7.4",
|
||||
"@types/node": "18.15.0",
|
||||
|
|
@ -1275,6 +1276,20 @@
|
|||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mantine/spotlight": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-6.0.2.tgz",
|
||||
"integrity": "sha512-MdC6qXivhrjiKPyM1WPwcaWIo8thfGpKMv9lNuzSKJRzF5n3Uiexucyrb1B+Ca6JqEWWMMuDJ4iNIijAQRhc/Q==",
|
||||
"dependencies": {
|
||||
"@mantine/utils": "6.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mantine/core": "6.0.2",
|
||||
"@mantine/hooks": "6.0.2",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mantine/ssr": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mantine/ssr/-/ssr-6.0.2.tgz",
|
||||
|
|
@ -8427,6 +8442,14 @@
|
|||
"react-transition-group": "4.4.2"
|
||||
}
|
||||
},
|
||||
"@mantine/spotlight": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-6.0.2.tgz",
|
||||
"integrity": "sha512-MdC6qXivhrjiKPyM1WPwcaWIo8thfGpKMv9lNuzSKJRzF5n3Uiexucyrb1B+Ca6JqEWWMMuDJ4iNIijAQRhc/Q==",
|
||||
"requires": {
|
||||
"@mantine/utils": "6.0.2"
|
||||
}
|
||||
},
|
||||
"@mantine/ssr": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mantine/ssr/-/ssr-6.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
"@emotion/react": "^11.10.6",
|
||||
"@emotion/server": "^11.10.0",
|
||||
"@mantine/core": "^6.0.1",
|
||||
"@mantine/spotlight": "^6.0.2",
|
||||
"@mantine/hooks": "^6.0.1",
|
||||
"@mantine/next": "^6.0.1",
|
||||
"@mantine/notifications": "^6.0.1",
|
||||
|
|
|
|||
BIN
public/assets/integrations/deta.png
Normal file
BIN
public/assets/integrations/deta.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from "@mantine/core";
|
||||
import { useClickOutside } from "@mantine/hooks";
|
||||
import { IconArrowForward, IconCopy, IconTrash, IconUser } from "@tabler/icons-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { Message, detaDB } from "../db";
|
||||
|
|
@ -22,6 +22,8 @@ import "../styles/markdown.scss";
|
|||
import { CreatePromptModal } from "./CreatePromptModal";
|
||||
import { LogoIcon } from "./Logo";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useSpaceAppActions } from "../hooks/contexts";
|
||||
import { SelectIntegrationModal } from "./SelectIntegrationModal";
|
||||
|
||||
export function MessageItem({ message, readOnly = false, onDeleted, handleUseMessage }: { message: Message, readOnly?: boolean, onDeleted?: (key: string) => void, handleUseMessage?: (key: string) => void }) {
|
||||
const wordCount = useMemo(() => {
|
||||
|
|
@ -29,9 +31,18 @@ export function MessageItem({ message, readOnly = false, onDeleted, handleUseMes
|
|||
return matches ? matches.length : 0;
|
||||
}, [message.content]);
|
||||
|
||||
const [isExpanded, setExpanded] = useState(false);
|
||||
const ref = useClickOutside(() => setExpanded(false));
|
||||
const theme = useMantineTheme();
|
||||
const markdownRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { spaceAppActions } = useSpaceAppActions()
|
||||
|
||||
const [isExpanded, setExpanded] = useState(false);
|
||||
const [html, setHtml] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
setHtml(markdownRef.current?.innerHTML || '')
|
||||
}, [ref])
|
||||
|
||||
const handleDelete = async () => {
|
||||
await detaDB.messages.delete(message.key);
|
||||
|
|
@ -66,7 +77,7 @@ export function MessageItem({ message, readOnly = false, onDeleted, handleUseMes
|
|||
</ThemeIcon>
|
||||
)}
|
||||
{message.role === "assistant" && <LogoIcon style={{ height: 32 }} color1={theme.colors[theme.primaryColor][3]} color2={theme.colors[theme.primaryColor][7]} />}
|
||||
<Box sx={{ flex: 1, width: 0 }} className="markdown">
|
||||
<Box sx={{ flex: 1, width: 0 }} className="markdown" ref={markdownRef}>
|
||||
<ReactMarkdown
|
||||
children={message.content}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
|
|
@ -111,6 +122,17 @@ export function MessageItem({ message, readOnly = false, onDeleted, handleUseMes
|
|||
</Box>
|
||||
|
||||
<Box style={{ display: 'flex' }}>
|
||||
{spaceAppActions !== null && (
|
||||
<SelectIntegrationModal contentHtml={html} contentText={message.content}>
|
||||
<Tooltip label="Send to Space" position="top">
|
||||
<ActionIcon >
|
||||
{/* <IconBolt opacity={0.5} size={20} /> */}
|
||||
<img src="/assets/integrations/deta.png" height={20} style={{ opacity: 0.75 }} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</SelectIntegrationModal>
|
||||
)}
|
||||
|
||||
{!readOnly && handleUseMessage && (
|
||||
<>
|
||||
<CreatePromptModal content={message.content || ''} />
|
||||
|
|
|
|||
231
src/components/SelectIntegrationModal.tsx
Normal file
231
src/components/SelectIntegrationModal.tsx
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
import { notifications } from "@mantine/notifications";
|
||||
import { SpotlightProvider, spotlight } from '@mantine/spotlight';
|
||||
import { cloneElement, ReactElement, useEffect, useState } from "react";
|
||||
import { useChat, useSpaceAppActions } from "../hooks/contexts";
|
||||
import { IconBolt, IconSearch } from "@tabler/icons-react";
|
||||
import { AppAction } from "../db";
|
||||
|
||||
export function SelectIntegrationModal({
|
||||
contentText,
|
||||
contentHtml,
|
||||
children,
|
||||
}: {
|
||||
contentText: string;
|
||||
contentHtml: string;
|
||||
children: ReactElement;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { chat } = useChat()
|
||||
const { setSpaceAppActions } = useSpaceAppActions()
|
||||
|
||||
const [spotlightActions, setSpotlightActions] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const dataFetch = async () => {
|
||||
const res = await fetch(`/api/space/actions`)
|
||||
const items = await res.json()
|
||||
const actions = items as unknown as AppAction[]
|
||||
|
||||
setSpaceAppActions(actions)
|
||||
|
||||
const spotlightActionsItems = actions.map(action => ({
|
||||
title: action.title,
|
||||
description: action.instance_alias,
|
||||
icon: action.icon_url ? <img width={28} style={{ borderRadius: 5 }} src={action.icon_url} alt={action.title} /> : <IconBolt size={28} />,
|
||||
onTrigger: async () => {
|
||||
try {
|
||||
if (action) {
|
||||
const titleKeys = ['title', 'name']
|
||||
const contentKeys = ['content', 'description', 'text']
|
||||
|
||||
let payload: { [key: string]: string } = {}
|
||||
for (const input of action.input!) {
|
||||
if (titleKeys.includes(input.name)) {
|
||||
payload[input.name] = chat?.description || 'Dialogue AI Chat'
|
||||
} else if (contentKeys.includes(input.name)) {
|
||||
payload[input.name] = action.app_name === 'Minima' ? contentHtml : contentText
|
||||
} else if (input.name === 'html') {
|
||||
payload[input.name] = contentHtml
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/space/actions/invoke`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
instanceId: action.instance_id,
|
||||
actionName: action.name,
|
||||
payload: payload
|
||||
})
|
||||
})
|
||||
|
||||
const invocation = await res.json()
|
||||
console.log(invocation)
|
||||
if (invocation.type === '@deta/raw') {
|
||||
navigator.clipboard.writeText(invocation.data)
|
||||
} else if (invocation.type === '@deta/detail') {
|
||||
navigator.clipboard.writeText(invocation.data.ref || invocation.data.url || invocation.data.title || invocation.data.text)
|
||||
} else {
|
||||
navigator.clipboard.writeText(JSON.stringify(invocation.data))
|
||||
}
|
||||
|
||||
notifications.show({
|
||||
title: "Success",
|
||||
color: "green",
|
||||
message: `Action "${action.title}" invoked successfully.`,
|
||||
})
|
||||
} else {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
color: "red",
|
||||
message: "Integration not found.",
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.toJSON().message === "Network Error") {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
color: "red",
|
||||
message: "No internet connection.",
|
||||
});
|
||||
}
|
||||
const message = error.response?.data?.error?.message;
|
||||
if (message) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
color: "red",
|
||||
message,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
setSpotlightActions(spotlightActionsItems)
|
||||
spotlight.open()
|
||||
}
|
||||
if (open) {
|
||||
dataFetch()
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{cloneElement(children, { onClick: handleOpen })}
|
||||
{open && (
|
||||
<SpotlightProvider
|
||||
actions={spotlightActions}
|
||||
searchIcon={<IconSearch size="1.2rem" />}
|
||||
searchPlaceholder="Search..."
|
||||
shortcut={null}
|
||||
nothingFoundMessage="Nothing found..."
|
||||
onSpotlightOpen={() => setOpen(true)}
|
||||
onSpotlightClose={() => setOpen(false)}
|
||||
limit={Infinity}
|
||||
>
|
||||
</SpotlightProvider>
|
||||
)}
|
||||
{/* <Modal opened={opened} onClose={close} title="Space App Actions" withinPortal size="lg">
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
event.preventDefault();
|
||||
|
||||
const action = spaceAppActions?.find(i => {
|
||||
const appName = selectedAppAction.split('__')[0]
|
||||
const actionName = selectedAppAction.split('__')[1]
|
||||
|
||||
return i.app_name === appName && i.name === actionName
|
||||
})
|
||||
|
||||
if (action) {
|
||||
|
||||
const titleKeys = ['title', 'name']
|
||||
const contentKeys = ['content', 'description', 'text']
|
||||
|
||||
let payload: { [key: string]: string } = {}
|
||||
for (const input of action.input!) {
|
||||
if (titleKeys.includes(input.name)) {
|
||||
payload[input.name] = chat?.description || 'Dialogue AI Chat'
|
||||
} else if (contentKeys.includes(input.name)) {
|
||||
payload[input.name] = contentHtml
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/space/actions/invoke`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
instanceId: action.instance_id,
|
||||
actionName: action.name,
|
||||
payload: payload
|
||||
})
|
||||
})
|
||||
|
||||
console.log(res)
|
||||
|
||||
navigator.clipboard.writeText('noteUrl')
|
||||
|
||||
notifications.show({
|
||||
title: "Success",
|
||||
color: "green",
|
||||
message: "Message stored in Minima and link copied to clipboard.",
|
||||
})
|
||||
} else {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
color: "red",
|
||||
message: "Integration not found.",
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.toJSON().message === "Network Error") {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
color: "red",
|
||||
message: "No internet connection.",
|
||||
});
|
||||
}
|
||||
const message = error.response?.data?.error?.message;
|
||||
if (message) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
color: "red",
|
||||
message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
setSelectedAppAction('');
|
||||
close();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Stack>
|
||||
<Select
|
||||
value={selectedAppAction}
|
||||
onChange={(value) => setSelectedAppAction(value ?? '')}
|
||||
data={(spaceAppActions || []).map(appAction => ({ value: `${appAction.app_name}__${appAction.name}`, label: `${appAction.app_name} ${appAction.title} - ${appAction.instance_alias}` }))}
|
||||
placeholder="Select App Action"
|
||||
variant="filled"
|
||||
searchable
|
||||
/>
|
||||
<Button type="submit" loading={submitting}>
|
||||
Send to App
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -47,4 +47,37 @@ export interface Chat {
|
|||
key: string;
|
||||
instance: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export enum ViewType {
|
||||
DETAIL = '@deta/detail',
|
||||
RAW = '@deta/raw',
|
||||
FILE = '@deta/file',
|
||||
LIST = '@deta/list',
|
||||
TABLE = '@deta/table',
|
||||
}
|
||||
|
||||
export type ActionInput = {
|
||||
name: string
|
||||
title?: string
|
||||
type?: string
|
||||
optional: boolean
|
||||
}
|
||||
|
||||
export type AppAction = {
|
||||
title: string
|
||||
name: string
|
||||
description: string
|
||||
local?: boolean
|
||||
output: ViewType
|
||||
app_name: string
|
||||
instance_id: string
|
||||
instance_alias: string
|
||||
channel: string
|
||||
version: string
|
||||
input?: ActionInput[]
|
||||
icon_url?: string
|
||||
placeholder_icon_config?: {
|
||||
css_background: string
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useContext } from "react";
|
||||
import type { Chat, Prompt, Settings } from "../db";
|
||||
import type { AppAction, Chat, Prompt, Settings } from "../db";
|
||||
|
||||
export const ChatContext = React.createContext<{
|
||||
chat: Chat | null,
|
||||
|
|
@ -44,4 +44,13 @@ export const IncognitoModeContext = React.createContext<{
|
|||
|
||||
export function useIncognitoMode() {
|
||||
return useContext(IncognitoModeContext);
|
||||
}
|
||||
|
||||
export const SpaceAppActionsContext = React.createContext<{
|
||||
spaceAppActions: AppAction[] | null,
|
||||
setSpaceAppActions: React.Dispatch<React.SetStateAction<AppAction[] | null>>
|
||||
}>({ spaceAppActions: null, setSpaceAppActions: () => {} });
|
||||
|
||||
export function useSpaceAppActions() {
|
||||
return useContext(SpaceAppActionsContext);
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ import { CreatePromptModal } from "../components/CreatePromptModal";
|
|||
import { LogoIcon } from "../components/Logo";
|
||||
import { SettingsModal } from "../components/SettingsModal";
|
||||
import { config } from "../utils/config";
|
||||
import { ChatContext, ChatsContext, IncognitoModeContext, PromptsContext, SettingsContext } from "../hooks/contexts";
|
||||
import { ChatContext, ChatsContext, IncognitoModeContext, PromptsContext, SettingsContext, SpaceAppActionsContext } from "../hooks/contexts";
|
||||
import { ChatHeader } from "../components/ChatHeader";
|
||||
import { useLocalStorage } from "@mantine/hooks";
|
||||
import { CreateChatButton } from "../components/CreateChatButton";
|
||||
|
|
@ -81,6 +81,20 @@ export function BaseLayout({ children }: { children: React.ReactNode }) {
|
|||
dataFetch();
|
||||
}, []);
|
||||
|
||||
const [spaceAppActions, setSpaceAppActions] = useState<any[] | null>(null);
|
||||
useEffect(() => {
|
||||
const dataFetch = async () => {
|
||||
const res = await fetch(`/api/space/actions/config`)
|
||||
const config = await res.json()
|
||||
|
||||
if (config.isSetup) {
|
||||
setSpaceAppActions([])
|
||||
}
|
||||
};
|
||||
|
||||
dataFetch();
|
||||
}, []);
|
||||
|
||||
const [chat, setChat] = useState<Chat | null>(null);
|
||||
useEffect(() => {
|
||||
const dataFetch = async () => {
|
||||
|
|
@ -163,180 +177,182 @@ export function BaseLayout({ children }: { children: React.ReactNode }) {
|
|||
|
||||
return (
|
||||
<SettingsContext.Provider value={{ settings: settings, setSettings: setSettings }}>
|
||||
<ChatContext.Provider value={{ chat: chat, setChat: setChat }}>
|
||||
<ChatsContext.Provider value={{ chats: chats, setChats: setChats }}>
|
||||
<PromptsContext.Provider value={{ prompts: prompts, setPrompts: setPrompts }}>
|
||||
<IncognitoModeContext.Provider value={{ incognitoMode: incognitoMode, setIncognitoMode: setIncognitoMode }}>
|
||||
<AppShell
|
||||
className={`${colorScheme}-theme`}
|
||||
navbarOffsetBreakpoint="sm"
|
||||
navbar={
|
||||
<Navbar width={{ md: 300 }} hiddenBreakpoint="md" hidden={!opened}>
|
||||
<Navbar.Section className="app-region-drag">
|
||||
<Box
|
||||
style={{
|
||||
height: 60,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: 10,
|
||||
borderBottom: border,
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
to="/"
|
||||
className="app-region-no-drag"
|
||||
style={{ padding: 4, textDecoration: 'none', color: 'inherit', display: 'flex', alignItems: 'center', gap: 8 }}
|
||||
<SpaceAppActionsContext.Provider value={{ spaceAppActions: spaceAppActions, setSpaceAppActions: setSpaceAppActions }}>
|
||||
<ChatContext.Provider value={{ chat: chat, setChat: setChat }}>
|
||||
<ChatsContext.Provider value={{ chats: chats, setChats: setChats }}>
|
||||
<PromptsContext.Provider value={{ prompts: prompts, setPrompts: setPrompts }}>
|
||||
<IncognitoModeContext.Provider value={{ incognitoMode: incognitoMode, setIncognitoMode: setIncognitoMode }}>
|
||||
<AppShell
|
||||
className={`${colorScheme}-theme`}
|
||||
navbarOffsetBreakpoint="sm"
|
||||
navbar={
|
||||
<Navbar width={{ md: 300 }} hiddenBreakpoint="md" hidden={!opened}>
|
||||
<Navbar.Section className="app-region-drag">
|
||||
<Box
|
||||
style={{
|
||||
height: 60,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: 10,
|
||||
borderBottom: border,
|
||||
}}
|
||||
>
|
||||
<LogoIcon style={{ height: 22, display: "block", }} color1={theme.colors[theme.primaryColor][3]} color2={theme.colors[theme.primaryColor][7]} />
|
||||
<Text size='1.25rem' weight={600}>
|
||||
Dialogue AI
|
||||
</Text>
|
||||
</Link>
|
||||
<Box style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{incognitoMode ? (
|
||||
<Tooltip label="Disable Incognito Mode">
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
onClick={handleIncognito}
|
||||
>
|
||||
<IconSpyOff size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip label="Incognito Mode">
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
onClick={handleIncognito}
|
||||
>
|
||||
<IconSpy size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
<SettingsModal>
|
||||
<Tooltip label="Settings">
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
sx={(theme) => ({
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
marginRight: '2.5rem',
|
||||
},
|
||||
})}
|
||||
<Link
|
||||
to="/"
|
||||
className="app-region-no-drag"
|
||||
style={{ padding: 4, textDecoration: 'none', color: 'inherit', display: 'flex', alignItems: 'center', gap: 8 }}
|
||||
>
|
||||
<LogoIcon style={{ height: 22, display: "block", }} color1={theme.colors[theme.primaryColor][3]} color2={theme.colors[theme.primaryColor][7]} />
|
||||
<Text size='1.25rem' weight={600}>
|
||||
Dialogue AI
|
||||
</Text>
|
||||
</Link>
|
||||
<Box style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{incognitoMode ? (
|
||||
<Tooltip label="Disable Incognito Mode">
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
onClick={handleIncognito}
|
||||
>
|
||||
<IconSettings size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</SettingsModal>
|
||||
<IconSpyOff size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip label="Incognito Mode">
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
onClick={handleIncognito}
|
||||
>
|
||||
<IconSpy size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
<SettingsModal>
|
||||
<Tooltip label="Settings">
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
sx={(theme) => ({
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
marginRight: '2.5rem',
|
||||
},
|
||||
})}
|
||||
>
|
||||
<IconSettings size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</SettingsModal>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Navbar.Section>
|
||||
<Navbar.Section
|
||||
sx={(theme) => ({
|
||||
padding: rem(4),
|
||||
background:
|
||||
theme.colorScheme === "dark"
|
||||
? theme.colors.dark[8]
|
||||
: theme.colors.gray[1],
|
||||
borderBottom: border,
|
||||
})}
|
||||
>
|
||||
<SegmentedControl
|
||||
fullWidth
|
||||
value={tab}
|
||||
onChange={(value) => setTab(value as typeof tab)}
|
||||
data={["Chats", "Prompts"]}
|
||||
/>
|
||||
</Navbar.Section>
|
||||
<Navbar.Section
|
||||
sx={(theme) => ({
|
||||
padding: rem(4),
|
||||
background:
|
||||
theme.colorScheme === "dark"
|
||||
? theme.colors.dark[7]
|
||||
: theme.white,
|
||||
borderBottom: border,
|
||||
})}
|
||||
>
|
||||
<TextInput
|
||||
variant="unstyled"
|
||||
radius={0}
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onChange={(event) =>
|
||||
setSearch(event.currentTarget.value.toLowerCase())
|
||||
}
|
||||
sx={{ paddingInline: 4 }}
|
||||
icon={<IconSearch opacity={0.8} size={20} />}
|
||||
rightSection={
|
||||
!!search && (
|
||||
<ActionIcon onClick={() => setSearch("")}>
|
||||
<IconX opacity={0.5} size={20} />{" "}
|
||||
</ActionIcon>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Navbar.Section>
|
||||
<Navbar.Section grow component={ScrollArea} id="chats">
|
||||
{tab === "Chats" && (fetchingChats ? (
|
||||
<Stack spacing="xs" mt={8} px={10}>
|
||||
<Skeleton height="2rem" />
|
||||
<Skeleton height="2rem" />
|
||||
<Skeleton height="2rem" />
|
||||
</Stack>
|
||||
) : (<Chats search={search} />))}
|
||||
{tab === "Prompts" && (
|
||||
<Suspense fallback={
|
||||
</Navbar.Section>
|
||||
<Navbar.Section
|
||||
sx={(theme) => ({
|
||||
padding: rem(4),
|
||||
background:
|
||||
theme.colorScheme === "dark"
|
||||
? theme.colors.dark[8]
|
||||
: theme.colors.gray[1],
|
||||
borderBottom: border,
|
||||
})}
|
||||
>
|
||||
<SegmentedControl
|
||||
fullWidth
|
||||
value={tab}
|
||||
onChange={(value) => setTab(value as typeof tab)}
|
||||
data={["Chats", "Prompts"]}
|
||||
/>
|
||||
</Navbar.Section>
|
||||
<Navbar.Section
|
||||
sx={(theme) => ({
|
||||
padding: rem(4),
|
||||
background:
|
||||
theme.colorScheme === "dark"
|
||||
? theme.colors.dark[7]
|
||||
: theme.white,
|
||||
borderBottom: border,
|
||||
})}
|
||||
>
|
||||
<TextInput
|
||||
variant="unstyled"
|
||||
radius={0}
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onChange={(event) =>
|
||||
setSearch(event.currentTarget.value.toLowerCase())
|
||||
}
|
||||
sx={{ paddingInline: 4 }}
|
||||
icon={<IconSearch opacity={0.8} size={20} />}
|
||||
rightSection={
|
||||
!!search && (
|
||||
<ActionIcon onClick={() => setSearch("")}>
|
||||
<IconX opacity={0.5} size={20} />{" "}
|
||||
</ActionIcon>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Navbar.Section>
|
||||
<Navbar.Section grow component={ScrollArea} id="chats">
|
||||
{tab === "Chats" && (fetchingChats ? (
|
||||
<Stack spacing="xs" mt={8} px={10}>
|
||||
<Skeleton height="2rem" />
|
||||
<Skeleton height="2rem" />
|
||||
<Skeleton height="2rem" />
|
||||
</Stack>
|
||||
}>
|
||||
<Prompts search={search} onPlay={() => setTab("Chats")} />
|
||||
</Suspense>
|
||||
)}
|
||||
</Navbar.Section>
|
||||
<Navbar.Section>
|
||||
<Flex direction="column" p={10} gap="xs">
|
||||
{tab === "Chats" && (
|
||||
<>
|
||||
{ incognitoMode && (
|
||||
<DeleteChatsModal />
|
||||
)}
|
||||
<CreateChatButton fullWidth>
|
||||
{incognitoMode ? "New Private Chat" : "New Chat"}
|
||||
</CreateChatButton>
|
||||
</>
|
||||
) : (<Chats search={search} />))}
|
||||
{tab === "Prompts" && (
|
||||
<Suspense fallback={
|
||||
<Stack spacing="xs" mt={8} px={10}>
|
||||
<Skeleton height="2rem" />
|
||||
<Skeleton height="2rem" />
|
||||
<Skeleton height="2rem" />
|
||||
</Stack>
|
||||
}>
|
||||
<Prompts search={search} onPlay={() => setTab("Chats")} />
|
||||
</Suspense>
|
||||
)}
|
||||
{tab === "Prompts" && <CreatePromptModal />}
|
||||
</Flex>
|
||||
</Navbar.Section>
|
||||
</Navbar>
|
||||
}
|
||||
header={
|
||||
chat ? (
|
||||
<ChatHeader />
|
||||
) : undefined
|
||||
}
|
||||
layout="alt"
|
||||
padding={0}
|
||||
>
|
||||
<MediaQuery largerThan="md" styles={{ display: "none" }}>
|
||||
<Burger
|
||||
opened={opened}
|
||||
onClick={() => setOpened((o) => !o)}
|
||||
size="sm"
|
||||
color={theme.colors.gray[6]}
|
||||
className="app-region-no-drag"
|
||||
sx={{ position: "fixed", top: 16, right: 16, zIndex: 100 }}
|
||||
/>
|
||||
</MediaQuery>
|
||||
{children}
|
||||
</AppShell>
|
||||
</IncognitoModeContext.Provider>
|
||||
</PromptsContext.Provider>
|
||||
</ChatsContext.Provider>
|
||||
</ChatContext.Provider>
|
||||
</Navbar.Section>
|
||||
<Navbar.Section>
|
||||
<Flex direction="column" p={10} gap="xs">
|
||||
{tab === "Chats" && (
|
||||
<>
|
||||
{ incognitoMode && (
|
||||
<DeleteChatsModal />
|
||||
)}
|
||||
<CreateChatButton fullWidth>
|
||||
{incognitoMode ? "New Private Chat" : "New Chat"}
|
||||
</CreateChatButton>
|
||||
</>
|
||||
)}
|
||||
{tab === "Prompts" && <CreatePromptModal />}
|
||||
</Flex>
|
||||
</Navbar.Section>
|
||||
</Navbar>
|
||||
}
|
||||
header={
|
||||
chat ? (
|
||||
<ChatHeader />
|
||||
) : undefined
|
||||
}
|
||||
layout="alt"
|
||||
padding={0}
|
||||
>
|
||||
<MediaQuery largerThan="md" styles={{ display: "none" }}>
|
||||
<Burger
|
||||
opened={opened}
|
||||
onClick={() => setOpened((o) => !o)}
|
||||
size="sm"
|
||||
color={theme.colors.gray[6]}
|
||||
className="app-region-no-drag"
|
||||
sx={{ position: "fixed", top: 16, right: 16, zIndex: 100 }}
|
||||
/>
|
||||
</MediaQuery>
|
||||
{children}
|
||||
</AppShell>
|
||||
</IncognitoModeContext.Provider>
|
||||
</PromptsContext.Provider>
|
||||
</ChatsContext.Provider>
|
||||
</ChatContext.Provider>
|
||||
</SpaceAppActionsContext.Provider>
|
||||
</SettingsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,6 +470,13 @@
|
|||
"@mantine/utils" "6.0.2"
|
||||
"react-transition-group" "4.4.2"
|
||||
|
||||
"@mantine/spotlight@^6.0.2":
|
||||
"integrity" "sha512-MdC6qXivhrjiKPyM1WPwcaWIo8thfGpKMv9lNuzSKJRzF5n3Uiexucyrb1B+Ca6JqEWWMMuDJ4iNIijAQRhc/Q=="
|
||||
"resolved" "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-6.0.2.tgz"
|
||||
"version" "6.0.2"
|
||||
dependencies:
|
||||
"@mantine/utils" "6.0.2"
|
||||
|
||||
"@mantine/ssr@6.0.2":
|
||||
"integrity" "sha512-s7F0LtKVdGflbV/2ofCUcdoEQlCpnspBIOPW2IORk7Ysab/XE/Ea2nphtfStNZcYZXk9haOVp50Yi8yAxzBOVg=="
|
||||
"resolved" "https://registry.npmjs.org/@mantine/ssr/-/ssr-6.0.2.tgz"
|
||||
|
|
|
|||
Loading…
Reference in a new issue