feat: 暂存添加ai chat and prompt generate

This commit is contained in:
xion 2024-09-26 01:18:04 +08:00
parent 3da62fd254
commit 553e4d62f0
16 changed files with 1265 additions and 60 deletions

View File

@ -26,8 +26,10 @@
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"copy-to-clipboard": "^3.3.3",
"d3": "^7.9.0",
"immer": "^10.1.1",
"lodash-es": "^4.17.21",
"marked": "^14.1.2",
"nanoid": "^5.0.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@ -41,6 +43,7 @@
"@eslint/js": "^9.11.0",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/typography": "^0.5.15",
"@types/d3": "^7.4.3",
"@types/node": "^22.5.5",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0",

439
pnpm-lock.yaml generated
View File

@ -50,12 +50,18 @@ importers:
copy-to-clipboard:
specifier: ^3.3.3
version: 3.3.3
d3:
specifier: ^7.9.0
version: 7.9.0
immer:
specifier: ^10.1.1
version: 10.1.1
lodash-es:
specifier: ^4.17.21
version: 4.17.21
marked:
specifier: ^14.1.2
version: 14.1.2
nanoid:
specifier: ^5.0.7
version: 5.0.7
@ -90,6 +96,9 @@ importers:
'@tailwindcss/typography':
specifier: ^0.5.15
version: 0.5.15(tailwindcss@3.4.13)
'@types/d3':
specifier: ^7.4.3
version: 7.4.3
'@types/node':
specifier: ^22.5.5
version: 22.5.5
@ -741,27 +750,105 @@ packages:
'@types/babel__traverse@7.20.6':
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
'@types/d3-array@3.2.1':
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
'@types/d3-axis@3.0.6':
resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
'@types/d3-brush@3.0.6':
resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
'@types/d3-chord@3.0.6':
resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
'@types/d3-color@3.1.3':
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
'@types/d3-contour@3.0.6':
resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
'@types/d3-delaunay@6.0.4':
resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
'@types/d3-dispatch@3.0.6':
resolution: {integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==}
'@types/d3-drag@3.0.7':
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
'@types/d3-dsv@3.0.7':
resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
'@types/d3-ease@3.0.2':
resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
'@types/d3-fetch@3.0.7':
resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
'@types/d3-force@3.0.10':
resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
'@types/d3-format@3.0.4':
resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
'@types/d3-geo@3.1.0':
resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
'@types/d3-hierarchy@3.1.7':
resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
'@types/d3-interpolate@3.0.4':
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
'@types/d3-path@3.1.0':
resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==}
'@types/d3-polygon@3.0.2':
resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
'@types/d3-quadtree@3.0.6':
resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
'@types/d3-random@3.0.3':
resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
'@types/d3-scale-chromatic@3.0.3':
resolution: {integrity: sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==}
'@types/d3-scale@4.0.8':
resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==}
'@types/d3-selection@3.0.10':
resolution: {integrity: sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==}
'@types/d3-shape@3.1.6':
resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==}
'@types/d3-time-format@4.0.3':
resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
'@types/d3-time@3.0.3':
resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==}
'@types/d3-timer@3.0.2':
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
'@types/d3-transition@3.0.8':
resolution: {integrity: sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==}
'@types/d3-zoom@3.0.8':
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
'@types/d3@7.4.3':
resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
'@types/geojson@7946.0.14':
resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==}
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
@ -1033,6 +1120,10 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
compute-scroll-into-view@3.1.0:
resolution: {integrity: sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==}
@ -1067,10 +1158,34 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
d3-axis@3.0.0:
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
engines: {node: '>=12'}
d3-brush@3.0.0:
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
engines: {node: '>=12'}
d3-chord@3.0.1:
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
engines: {node: '>=12'}
d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
d3-contour@4.0.2:
resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
engines: {node: '>=12'}
d3-delaunay@6.0.4:
resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
engines: {node: '>=12'}
d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
@ -1079,18 +1194,79 @@ packages:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
engines: {node: '>=12'}
d3-dsv@3.0.1:
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
engines: {node: '>=12'}
hasBin: true
d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
d3-fetch@3.0.1:
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
engines: {node: '>=12'}
d3-force@3.0.0:
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
engines: {node: '>=12'}
d3-format@3.1.0:
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
engines: {node: '>=12'}
d3-geo@3.1.1:
resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
engines: {node: '>=12'}
d3-hierarchy@3.1.2:
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
engines: {node: '>=12'}
d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
d3-path@3.1.0:
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
engines: {node: '>=12'}
d3-polygon@3.0.1:
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
engines: {node: '>=12'}
d3-quadtree@3.0.1:
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
engines: {node: '>=12'}
d3-random@3.0.1:
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
engines: {node: '>=12'}
d3-scale-chromatic@3.1.0:
resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
engines: {node: '>=12'}
d3-scale@4.0.2:
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
engines: {node: '>=12'}
d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
d3-shape@3.2.0:
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
engines: {node: '>=12'}
d3-time-format@4.1.0:
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
engines: {node: '>=12'}
d3-time@3.1.0:
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
engines: {node: '>=12'}
d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
@ -1105,6 +1281,10 @@ packages:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
d3@7.9.0:
resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
engines: {node: '>=12'}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
@ -1123,6 +1303,9 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
delaunator@5.0.1:
resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@ -1360,6 +1543,10 @@ packages:
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@ -1378,6 +1565,10 @@ packages:
inline-style-parser@0.2.4:
resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
@ -1515,6 +1706,11 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
marked@14.1.2:
resolution: {integrity: sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==}
engines: {node: '>= 18'}
hasBin: true
mdast-util-to-hast@13.2.0:
resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
@ -2030,6 +2226,9 @@ packages:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
rollup@4.22.0:
resolution: {integrity: sha512-W21MUIFPZ4+O2Je/EU+GP3iz7PH4pVPUXSbEZdatQnxo29+3rsUjgrJmzuAZU24z7yRAnFN6ukxeAhZh/c7hzg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -2038,6 +2237,12 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
rw@1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@ -2966,18 +3171,81 @@ snapshots:
dependencies:
'@babel/types': 7.25.6
'@types/d3-array@3.2.1': {}
'@types/d3-axis@3.0.6':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-brush@3.0.6':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-chord@3.0.6': {}
'@types/d3-color@3.1.3': {}
'@types/d3-contour@3.0.6':
dependencies:
'@types/d3-array': 3.2.1
'@types/geojson': 7946.0.14
'@types/d3-delaunay@6.0.4': {}
'@types/d3-dispatch@3.0.6': {}
'@types/d3-drag@3.0.7':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-dsv@3.0.7': {}
'@types/d3-ease@3.0.2': {}
'@types/d3-fetch@3.0.7':
dependencies:
'@types/d3-dsv': 3.0.7
'@types/d3-force@3.0.10': {}
'@types/d3-format@3.0.4': {}
'@types/d3-geo@3.1.0':
dependencies:
'@types/geojson': 7946.0.14
'@types/d3-hierarchy@3.1.7': {}
'@types/d3-interpolate@3.0.4':
dependencies:
'@types/d3-color': 3.1.3
'@types/d3-path@3.1.0': {}
'@types/d3-polygon@3.0.2': {}
'@types/d3-quadtree@3.0.6': {}
'@types/d3-random@3.0.3': {}
'@types/d3-scale-chromatic@3.0.3': {}
'@types/d3-scale@4.0.8':
dependencies:
'@types/d3-time': 3.0.3
'@types/d3-selection@3.0.10': {}
'@types/d3-shape@3.1.6':
dependencies:
'@types/d3-path': 3.1.0
'@types/d3-time-format@4.0.3': {}
'@types/d3-time@3.0.3': {}
'@types/d3-timer@3.0.2': {}
'@types/d3-transition@3.0.8':
dependencies:
'@types/d3-selection': 3.0.10
@ -2987,8 +3255,43 @@ snapshots:
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.10
'@types/d3@7.4.3':
dependencies:
'@types/d3-array': 3.2.1
'@types/d3-axis': 3.0.6
'@types/d3-brush': 3.0.6
'@types/d3-chord': 3.0.6
'@types/d3-color': 3.1.3
'@types/d3-contour': 3.0.6
'@types/d3-delaunay': 6.0.4
'@types/d3-dispatch': 3.0.6
'@types/d3-drag': 3.0.7
'@types/d3-dsv': 3.0.7
'@types/d3-ease': 3.0.2
'@types/d3-fetch': 3.0.7
'@types/d3-force': 3.0.10
'@types/d3-format': 3.0.4
'@types/d3-geo': 3.1.0
'@types/d3-hierarchy': 3.1.7
'@types/d3-interpolate': 3.0.4
'@types/d3-path': 3.1.0
'@types/d3-polygon': 3.0.2
'@types/d3-quadtree': 3.0.6
'@types/d3-random': 3.0.3
'@types/d3-scale': 4.0.8
'@types/d3-scale-chromatic': 3.0.3
'@types/d3-selection': 3.0.10
'@types/d3-shape': 3.1.6
'@types/d3-time': 3.0.3
'@types/d3-time-format': 4.0.3
'@types/d3-timer': 3.0.2
'@types/d3-transition': 3.0.8
'@types/d3-zoom': 3.0.8
'@types/estree@1.0.5': {}
'@types/geojson@7946.0.14': {}
'@types/hast@2.3.10':
dependencies:
'@types/unist': 2.0.11
@ -3348,6 +3651,8 @@ snapshots:
commander@4.1.1: {}
commander@7.2.0: {}
compute-scroll-into-view@3.1.0: {}
concat-map@0.0.1: {}
@ -3380,8 +3685,34 @@ snapshots:
csstype@3.1.3: {}
d3-array@3.2.4:
dependencies:
internmap: 2.0.3
d3-axis@3.0.0: {}
d3-brush@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
d3-chord@3.0.1:
dependencies:
d3-path: 3.1.0
d3-color@3.1.0: {}
d3-contour@4.0.2:
dependencies:
d3-array: 3.2.4
d3-delaunay@6.0.4:
dependencies:
delaunator: 5.0.1
d3-dispatch@3.0.1: {}
d3-drag@3.0.0:
@ -3389,14 +3720,71 @@ snapshots:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
d3-dsv@3.0.1:
dependencies:
commander: 7.2.0
iconv-lite: 0.6.3
rw: 1.3.3
d3-ease@3.0.1: {}
d3-fetch@3.0.1:
dependencies:
d3-dsv: 3.0.1
d3-force@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-quadtree: 3.0.1
d3-timer: 3.0.1
d3-format@3.1.0: {}
d3-geo@3.1.1:
dependencies:
d3-array: 3.2.4
d3-hierarchy@3.1.2: {}
d3-interpolate@3.0.1:
dependencies:
d3-color: 3.1.0
d3-path@3.1.0: {}
d3-polygon@3.0.1: {}
d3-quadtree@3.0.1: {}
d3-random@3.0.1: {}
d3-scale-chromatic@3.1.0:
dependencies:
d3-color: 3.1.0
d3-interpolate: 3.0.1
d3-scale@4.0.2:
dependencies:
d3-array: 3.2.4
d3-format: 3.1.0
d3-interpolate: 3.0.1
d3-time: 3.1.0
d3-time-format: 4.1.0
d3-selection@3.0.0: {}
d3-shape@3.2.0:
dependencies:
d3-path: 3.1.0
d3-time-format@4.1.0:
dependencies:
d3-time: 3.1.0
d3-time@3.1.0:
dependencies:
d3-array: 3.2.4
d3-timer@3.0.1: {}
d3-transition@3.0.1(d3-selection@3.0.0):
@ -3416,6 +3804,39 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
d3@7.9.0:
dependencies:
d3-array: 3.2.4
d3-axis: 3.0.0
d3-brush: 3.0.0
d3-chord: 3.0.1
d3-color: 3.1.0
d3-contour: 4.0.2
d3-delaunay: 6.0.4
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-dsv: 3.0.1
d3-ease: 3.0.1
d3-fetch: 3.0.1
d3-force: 3.0.0
d3-format: 3.1.0
d3-geo: 3.1.1
d3-hierarchy: 3.1.2
d3-interpolate: 3.0.1
d3-path: 3.1.0
d3-polygon: 3.0.1
d3-quadtree: 3.0.1
d3-random: 3.0.1
d3-scale: 4.0.2
d3-scale-chromatic: 3.1.0
d3-selection: 3.0.0
d3-shape: 3.2.0
d3-time: 3.1.0
d3-time-format: 4.1.0
d3-timer: 3.0.1
d3-transition: 3.0.1(d3-selection@3.0.0)
d3-zoom: 3.0.0
dayjs@1.11.13: {}
debug@4.3.7:
@ -3428,6 +3849,10 @@ snapshots:
deep-is@0.1.4: {}
delaunator@5.0.1:
dependencies:
robust-predicates: 3.0.2
dequal@2.0.3: {}
devlop@1.1.0:
@ -3729,6 +4154,10 @@ snapshots:
html-void-elements@3.0.0: {}
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
ignore@5.3.2: {}
immer@10.1.1: {}
@ -3742,6 +4171,8 @@ snapshots:
inline-style-parser@0.2.4: {}
internmap@2.0.3: {}
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
@ -3848,6 +4279,8 @@ snapshots:
dependencies:
yallist: 3.1.1
marked@14.1.2: {}
mdast-util-to-hast@13.2.0:
dependencies:
'@types/hast': 3.0.4
@ -4460,6 +4893,8 @@ snapshots:
reusify@1.0.4: {}
robust-predicates@3.0.2: {}
rollup@4.22.0:
dependencies:
'@types/estree': 1.0.5
@ -4486,6 +4921,10 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
rw@1.3.3: {}
safer-buffer@2.1.2: {}
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0

View File

@ -0,0 +1,38 @@
import { useShallow } from 'zustand/react/shallow';
import { useAiStore } from './store/ai-store';
import { CloseOutlined } from '@ant-design/icons';
import { Button } from 'antd';
export const AiMoudle = () => {
const aiStore = useAiStore(
useShallow((state) => {
return {
open: state.open,
setOpen: state.setOpen,
runAi: state.runAi,
};
}),
);
if (!aiStore.open) {
return null;
}
return (
<div className='w-96 flex-shrink-0 bg-gray-100 border-l-2 shadow-lg flex flex-col'>
<div className='flex gap-4 bg-slate-400 p-2'>
<Button className='position ml-4 !bg-slate-400 !border-black' onClick={() => aiStore.setOpen(false)} icon={<CloseOutlined />}></Button>
<h1 className='ml-10'>Ai Moudle</h1>
</div>
<div className='flex-grow p-2'>
<div> chat message</div>
<div>
<Button
onClick={() => {
aiStore.runAi();
}}>
Send
</Button>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,7 @@
import { AiMoudle } from './AiModule';
import { useAiStore } from './store/ai-store';
export { AiMoudle, useAiStore };
export const App = () => {
return <div>AI Chat</div>;
};

View File

@ -0,0 +1,58 @@
import { query } from '@/modules';
import { message } from 'antd';
import { create } from 'zustand';
export type AiStore = {
open: boolean;
setOpen: (open: boolean) => void;
type?: string;
key: string;
setKey: (key: string) => void;
setType?: (type: string) => void;
sendMsg: (msg: string) => void;
formData: any;
setFormData: (data: any) => void;
runAi: () => any;
title: string;
setTitle: (title: string) => void;
};
export const useAiStore = create<AiStore>((set, get) => {
return {
open: false,
setOpen: (open) => set({ open }),
key: '',
setKey: (key) => set({ key }),
sendMsg: (msg) => {
console.log(msg);
},
formData: {},
setFormData: (data) => set({ formData: data }),
runAi: async () => {
const { formData } = get();
const res = await query.post({
path: 'ai',
key: 'run',
data: {
key: formData.key,
inputs: [
{
key: 'title',
value: '根据描述生成代码',
},
{
key: 'description',
value: '我想获取一个card, 包含标题和内容标题是evision内容是这是一个测试',
},
],
},
});
if (res.code === 200) {
console.log(res.data);
message.success('Success');
}
},
title: '',
setTitle: (title) => set({ title }),
};
});

View File

@ -11,6 +11,8 @@ type Props = {
style?: React.CSSProperties;
language?: string;
listen?: boolean;
placeholder?: string;
onBlur?: () => void;
};
export const TextArea = (props: Props) => {
const [code, setCode] = useState<string>('');
@ -27,12 +29,13 @@ export const TextArea = (props: Props) => {
<div className={clsx('min-h-16 max-h-52 overflow-scroll scrollbar p-1 ', props.className)}>
<CodeEditor
value={code}
language='js'
language={props.language || 'js'}
className='border rounded-sm '
readOnly={props.readonly}
placeholder='Please enter JS code.'
placeholder={props.placeholder || 'Please enter JS code.'}
onChange={(evn) => _onChange(evn.target.value)}
padding={10}
onBlur={props.onBlur}
style={{
// backgroundColor: '#f5f5f5',
fontFamily: 'ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace',

View File

@ -59,12 +59,7 @@ const FormModal = () => {
<Input />
</Form.Item>
<Form.Item name='code' label='code'>
<TextArea
value={containerStore.formData.code}
style={{
height: '200px',
}}
/>
<TextArea />
</Form.Item>
<Form.Item label=' ' colon={false}>
<Button type='primary' htmlType='submit'>

41
src/pages/prompt/D3.tsx Normal file
View File

@ -0,0 +1,41 @@
import { query } from '@/modules';
import { useEffect } from 'react';
import { drawGraph } from './graph/d3';
export const D3Grahp = () => {
const get = async () => {
const res = await query.post({
path: 'prompt',
key: 'list',
});
console.log(res);
const grahpData = {
nodes: res.data.map((item) => {
return {
id: item.id,
label: item.title,
type: 'prompt',
};
}),
links: [],
};
drawGraph(grahpData);
};
const getD3 = async () => {
const res = await query.post({
path: 'prompt',
key: 'getD3',
});
drawGraph(res.data);
};
useEffect(() => {
getD3();
return () => {
document.querySelector('.ai-graph')!.innerHTML = '';
};
}, []);
return (
<div className='w-full h-full'>
<svg className='ai-graph border shadow-sm p-2 mx-auto mt-10 ' width='960' height='600' ></svg>
</div>
);
};

View File

@ -0,0 +1,57 @@
import { Button, Form, Input } from 'antd';
import { TextArea } from '../../container/components/TextArea';
import clsx from 'clsx';
export const Edit = () => {
const [form] = Form.useForm();
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onSave = () => {
//
};
const isEdit = form.getFieldValue('id');
return (
<div className='w-full h-full felx flex-col bg-gray-200'>
<h1 className='text-center py-4'>Prompt JS Code Generate</h1>
<div className='py-2 px-4 w-3/4 min-w-[600px] mx-auto border shadow rounded bg-white'>
<Form form={form} onFinish={onFinish} className='mt-4' labelCol={{ span: 4 }}>
<Form.Item name='id' hidden>
<Input />
</Form.Item>
<Form.Item name='title' label='Title'>
<Input />
</Form.Item>
<Form.Item name='description' label='Description'>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item name='code' label='Code'>
<TextArea className='max-h-full' style={{ minHeight: 300 }} />
</Form.Item>
<Form.Item label=' ' colon={false}>
<div className='flex gap-2'>
<Button type='primary' htmlType='submit'>
Generate
</Button>
<Button htmlType='reset'>Reset</Button>
<Button
type='primary'
onClick={() => {
//
}}>
Save
</Button>
<Button
className={clsx(isEdit ? 'block' : 'hidden')}
onClick={() => {
//
}}>
Preview
</Button>
</div>
</Form.Item>
</Form>
</div>
</div>
);
};

View File

@ -0,0 +1,342 @@
import { Button, Input, message, Modal, Table } from 'antd';
import { Fragment, useEffect, useMemo, useState } from 'react';
import { usePromptStore } from '../store/prompt';
import { useShallow } from 'zustand/react/shallow';
import { Form } from 'antd';
import copy from 'copy-to-clipboard';
import { useNavigate } from 'react-router';
import { EditOutlined, SettingOutlined, LinkOutlined, SaveOutlined, DeleteOutlined, LeftOutlined, CaretRightOutlined, PlusOutlined } from '@ant-design/icons';
import clsx from 'clsx';
import { TextArea } from '@/pages/container/components/TextArea';
import { marked } from 'marked';
import { extractKeysFromBraces } from '@/utils/extra';
import { useAiStore } from '@/pages/ai-chat';
const FormModal = () => {
const [form] = Form.useForm();
const promptStore = usePromptStore(
useShallow((state) => {
return {
showEdit: state.showEdit,
setShowEdit: state.setShowEdit,
formData: state.formData,
updateData: state.updateData,
runAi: state.runAi,
};
}),
);
useEffect(() => {
const open = promptStore.showEdit;
if (open) {
const isNull = JSON.stringify(promptStore.formData) === '{}';
if (isNull) {
form.resetFields();
} else {
form.setFieldsValue(promptStore.formData || {});
}
}
}, [promptStore.showEdit]);
const onFinish = async (values: any) => {
let other = {};
if (!values.id) {
other = {
presetData: {
validator: {},
data: {
prompt: '',
inputs: [],
},
},
};
}
promptStore.updateData({
...values,
...other,
});
};
const onClose = () => {
promptStore.setShowEdit(false);
form.resetFields();
};
const isEdit = promptStore.formData.id;
return (
<Modal
title={isEdit ? 'Edit' : 'Add'}
open={promptStore.showEdit}
onClose={() => promptStore.setShowEdit(false)}
destroyOnClose
footer={false}
width={800}
onCancel={onClose}>
<Form
form={form}
onFinish={onFinish}
labelCol={{
span: 4,
}}
wrapperCol={{
span: 20,
}}>
<Form.Item name='id' hidden>
<Input />
</Form.Item>
<Form.Item name='title' label='title'>
<Input />
</Form.Item>
<Form.Item name='description' label='description'>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item name='key' label='key'>
<Input />
</Form.Item>
<Form.Item label=' ' colon={false}>
<Button type='primary' htmlType='submit'>
Submit
</Button>
<Button className='ml-2' htmlType='reset' onClick={onClose}>
Cancel
</Button>
</Form.Item>
</Form>
</Modal>
);
};
export const List = () => {
const navicate = useNavigate();
const aiStore = useAiStore(
useShallow((state) => {
return {
open: state.open,
setOpen: state.setOpen,
key: state.key,
setKey: state.setKey,
sendMsg: state.sendMsg,
};
}),
);
const promptStore = usePromptStore(
useShallow((state) => {
return {
setFormData: state.setFormData,
setShowEdit: state.setShowEdit,
list: state.list,
deleteData: state.deleteData,
getList: state.getList,
loading: state.loading,
updateData: state.updateData,
formData: state.formData,
runAi: state.runAi,
};
}),
);
const [codeEdit, setCodeEdit] = useState(false);
const [code, setCode] = useState('');
const [form] = Form.useForm<{ inputs: any[] }>();
useEffect(() => {
promptStore.getList();
}, []);
useEffect(() => {
if (!codeEdit) {
form.setFieldsValue({ inputs: [] });
}
}, [codeEdit]);
const onAdd = () => {
promptStore.setFormData({});
promptStore.setShowEdit(true);
setCodeEdit(false);
};
const getFormInputs = () => {
if (!codeEdit) return;
console.log('blur getFormInputs');
const keys = extractKeysFromBraces(code);
const inputs = form.getFieldValue('inputs') || [];
const newInputs = keys
.map((key) => {
const has = inputs.some((item: any) => item.key === key);
if (!has) {
return {
key,
value: '',
};
}
return null;
})
.filter(Boolean);
if (newInputs.length > 0) {
form.setFieldsValue({ inputs: [...inputs, ...newInputs] });
}
};
const len = form.getFieldValue('inputs')?.length || 0;
return (
<div className='w-full h-full flex flex-col'>
<div className='flex flex-grow overflow-hidden h-full'>
<Button onClick={onAdd} type='primary' className='m-4 w-64 flex-shrink-0' icon={<PlusOutlined />}></Button>
<div className='flex-grow overflow-auto scrollbar bg-gray-100'>
<div className='flex flex-wrap gap-x-10 gap-y-4 rounded pt-10 justify-center'>
{promptStore.list.length > 0 &&
promptStore.list.map((item) => {
const { presetData } = item;
const md = presetData?.data?.prompt || '';
const inputs = presetData?.data?.inputs || [];
const html = marked.parse(md);
return (
<Fragment key={item.id}>
<div className='flex text-sm gap flex-col w-[600px] max-h-[400px] bg-white p-4 rounded-lg' key={item.id} onClick={() => {}}>
<div
className='px-4 cursor-pointer'
onClick={() => {
setCode(md);
promptStore.setFormData(item);
form.setFieldsValue({
inputs: inputs.map((item) => {
return { key: item.key, value: item.value };
}),
});
setCodeEdit(true);
}}>
<div
className='font-bold flex'
onClick={(e) => {
// copy(item.code);
// e.stopPropagation();
// message.success('copy code success');
}}>
{item.title || '-'}
<div
className=' ml-3 text-xs text-gray-400'
style={{
fontFamily: 'D-DIN',
}}>
{item?.key ? item.key : '-'}
</div>
</div>
<div className='font-light mt-2'>{item.description ? item.description : '-'}</div>
</div>
{/* <div className='w-full text-xs'>
<TextArea className='max-h-[240px] scrollbar' value={item.code} readonly />
</div> */}
<div className='px-4 mt-2'>{md ? 'Prompt' : ''}</div>
<div className='px-4'>
<div className='max-h-52 overflow-scroll scrollbar p-4 border shadow-sm mt-1'>
<div dangerouslySetInnerHTML={{ __html: html }}></div>
</div>
</div>
<div className='flex mt-4 ml-4'>
<Button.Group>
<Button
onClick={(e) => {
promptStore.setFormData(item);
promptStore.setShowEdit(true);
setCodeEdit(false);
e.stopPropagation();
}}
icon={<EditOutlined />}></Button>
<Button
onClick={(e) => {
promptStore.deleteData(item.id);
e.stopPropagation();
}}
icon={<DeleteOutlined />}></Button>
<Button
icon={<CaretRightOutlined />}
onClick={() => {
// navicate(`/prompt/${item.id}`);
promptStore.setFormData(item);
// promptStore.runAi();
aiStore.setOpen(true);
}}
/>
</Button.Group>
</div>
</div>
</Fragment>
);
})}
{new Array(4).fill(0).map((_, index) => {
return <div key={index} className='w-[600px]'></div>;
})}
{promptStore.list.length == 0 && (
<div className='text-center' key={'no-data'}>
No Data
</div>
)}
</div>
</div>
<div className={clsx('bg-gray-100 border-l flex flex-col border-bg-slate-300 w-[600px] flex-shrink-0', !codeEdit && 'hidden')}>
<div className='bg-white p-2'>
<div className='mt-2 ml-2 flex gap-2'>
<Button
onClick={() => {
setCodeEdit(false);
promptStore.setFormData({});
}}
icon={<LeftOutlined />}></Button>
<Button
onClick={() => {
// console.log('save', promptStore.formData);
const { presetData } = promptStore.formData;
const inputs = form.getFieldValue('inputs') || [];
promptStore.updateData({ ...promptStore.formData, presetData: { ...presetData, data: { ...presetData.data, prompt: code, inputs } } });
}}
icon={<SaveOutlined />}></Button>
</div>
</div>
<div className='flex-grow p-2 rounded-2 shadow-sm overflow-hidden'>
<TextArea
value={code}
language='markdown'
placeholder='Please enter markdown code.'
onChange={(value) => {
setCode(value);
}}
onBlur={() => {
console.log('blur');
setTimeout(() => {
getFormInputs();
}, 400);
}}
className='h-full max-h-full scrollbar'
style={{
overflow: 'auto',
minHeight: '200px',
// height: '100%', // height: '100%' 有bug
}}
/>
</div>
<div className='flex-shrink-0 px-4 mt-4'>
<h1 className={clsx('mb-2', len === 0 && 'hidden')}>Match Keys</h1>
<Form form={form}>
<Form.List name='inputs'>
{(fields, { add, remove }) => {
return (
<>
{fields.map((field, index) => {
return (
<div key={field.name + '-' + index} className='flex gap-2'>
<Form.Item name={[field.name, 'key']} rules={[{ required: true, message: 'Missing name' }]}>
<Input placeholder='name' />
</Form.Item>
<Form.Item name={[field.name, 'value']} rules={[{ required: true, message: 'Missing value' }]}>
<Input placeholder='value' />
</Form.Item>
{/* <Button onClick={() => add()} className='flex items-center'>
+
</Button> */}
<Button onClick={() => remove(field.name)}>-</Button>
</div>
);
})}
</>
);
}}
</Form.List>
</Form>
</div>
</div>
</div>
<FormModal />
</div>
);
};

View File

@ -0,0 +1,18 @@
.node circle {
fill: #69b3a2;
stroke: #333;
stroke-width: 1.5px;
}
.link {
fill: none;
stroke: #999;
stroke-opacity: 0.6;
stroke-width: 1.5px;
}
text {
font-family: Arial, sans-serif;
font-size: 12px;
pointer-events: none;
}

View File

@ -0,0 +1,122 @@
// @ts-nocheck
import * as d3 from 'd3';
import './d3.css';
export const drawGraph = (graphData) => {
// 初始配置:宽度和高度通过容器自适应
const svg = d3.select('.ai-graph');
const margin = { top: 20, right: 20, bottom: 20, left: 20 };
const updateChartSize = () => {
const width = svg.node().clientWidth - margin.left - margin.right;
const height = svg.node().clientHeight - margin.top - margin.bottom;
svg.attr('viewBox', `0 0 ${width} ${height}`);
return { width, height };
};
let { width, height } = updateChartSize();
// 使用力导向布局
const simulation = d3
.forceSimulation(graphData.nodes)
.force(
'link',
d3
.forceLink(graphData.links)
.id((d) => d.id)
.distance(100)
)
.force('charge', d3.forceManyBody().strength(-300))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('collide', d3.forceCollide(20)); // 防止节点重叠半径设置为20
// 绘制连线
const link = svg.append('g')
.selectAll('line')
.data(graphData.links)
.enter()
.append('line')
.attr('class', 'link');
// 绘制节点
const node = svg.append('g')
.selectAll('g')
.data(graphData.nodes)
.enter()
.append('g')
.attr('class', 'node');
node.append('circle').attr('r', 10);
// 添加节点标签
node
.append('text')
.attr('dx', 12)
.attr('dy', '.35em')
.text((d) => d.label);
// 限制节点在SVG范围内
const clampPosition = (d, width, height) => {
d.x = Math.max(10, Math.min(width - 10, d.x)); // 10为节点半径
d.y = Math.max(10, Math.min(height - 10, d.y));
};
// 更新节点和连线位置
simulation.on('tick', () => {
link
.attr('x1', (d) => d.source.x)
.attr('y1', (d) => d.source.y)
.attr('x2', (d) => d.target.x)
.attr('y2', (d) => d.target.y);
node.attr('transform', (d) => {
// 限制节点在SVG内部
clampPosition(d, width, height);
return `translate(${d.x},${d.y})`;
});
});
// 添加拖拽事件
node.call(
d3
.drag()
.on('start', (event, d) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
})
.on('drag', (event, d) => {
d.fx = event.x;
d.fy = event.y;
})
.on('end', (event, d) => {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
})
);
// 添加双击事件
node.on('dblclick', (event, d) => {
d.fx = null;
d.fy = null;
console.log('dblclick', d);
});
// 监听窗口大小变化时更新图表
const resize = () => {
const { width, height } = updateChartSize();
simulation.force('center', d3.forceCenter(width / 2, height / 2));
simulation.alpha(1).restart(); // 重启仿真以更新布局
};
window.addEventListener('resize', resize);
// 在需要的时候手动调用,销毁图表时可以移除监听
return () => {
window.removeEventListener('resize', resize);
};
};

View File

@ -1,57 +1,18 @@
import { Button, Form, Input } from 'antd';
import { TextArea } from '../container/components/TextArea';
import clsx from 'clsx';
import { Routes, Route, Navigate } from 'react-router-dom';
import { Edit } from './edit/Edit';
import { List } from './edit/List';
import { D3Grahp } from './D3';
import { Main } from './layout/Main';
export const App = () => {
const [form] = Form.useForm();
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onSave = () => {
//
};
const isEdit = form.getFieldValue('id');
return (
<div className='w-full h-full felx flex-col bg-gray-200'>
<h1 className='text-center py-4'>Prompt JS Code Generate</h1>
<div className='py-2 px-4 w-3/4 min-w-[600px] mx-auto border shadow rounded bg-white'>
<Form form={form} onFinish={onFinish} className='mt-4' labelCol={{ span: 4 }}>
<Form.Item name='id' hidden>
<Input />
</Form.Item>
<Form.Item name='title' label='Title'>
<Input />
</Form.Item>
<Form.Item name='description' label='Description'>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item name='code' label='Code'>
<TextArea className='max-h-full' style={{ minHeight: 300 }} />
</Form.Item>
<Form.Item label=' ' colon={false}>
<div className='flex gap-2'>
<Button type='primary' htmlType='submit'>
Generate
</Button>
<Button htmlType='reset'>Reset</Button>
<Button
type='primary'
onClick={() => {
//
}}>
Save
</Button>
<Button
className={clsx(isEdit ? 'block' : 'hidden')}
onClick={() => {
//
}}>
Preview
</Button>
</div>
</Form.Item>
</Form>
</div>
</div>
<Routes>
<Route element={<Main />}>
<Route path='/' element={<Navigate to='/prompt/list' />} />
<Route path='/graph' element={<D3Grahp />} />
<Route path='/edit' element={<Edit />} />
<Route path='/list' element={<List />} />
</Route>
</Routes>
);
};

View File

@ -0,0 +1,13 @@
import { AiMoudle } from '@/pages/ai-chat';
import { Outlet } from 'react-router';
export const Main = () => {
return (
<div className='w-full h-full flex'>
<div className='flex-grow h-full'>
<Outlet />
</div>
<AiMoudle />
</div>
);
};

View File

@ -0,0 +1,97 @@
import { create } from 'zustand';
import { query } from '@/modules';
import { message } from 'antd';
type PromptStore = {
showEdit: boolean;
setShowEdit: (showEdit: boolean) => void;
formData: any;
setFormData: (formData: any) => void;
loading: boolean;
setLoading: (loading: boolean) => void;
list: any[];
getList: () => Promise<void>;
updateData: (data: any) => Promise<void>;
deleteData: (id: string) => Promise<void>;
runAi: () => any;
};
export const usePromptStore = create<PromptStore>((set, get) => {
return {
showEdit: false,
setShowEdit: (showEdit) => set({ showEdit }),
formData: {},
setFormData: (formData) => set({ formData }),
loading: false,
setLoading: (loading) => set({ loading }),
list: [],
getList: async () => {
set({ loading: true });
const res = await query.post({
path: 'prompt',
key: 'list',
});
set({ loading: false });
if (res.code === 200) {
set({ list: res.data });
} else {
message.error(res.msg || 'Request failed');
}
},
updateData: async (data) => {
const { getList } = get();
const res = await query.post({
path: 'prompt',
key: 'update',
data,
});
if (res.code === 200) {
message.success('Success');
set({ showEdit: false, formData: [] });
getList();
} else {
message.error(res.msg || 'Request failed');
}
},
deleteData: async (id) => {
const { getList } = get();
const res = await query.post({
path: 'prompt',
key: 'delete',
id,
});
if (res.code === 200) {
getList();
message.success('Success');
} else {
message.error(res.msg || 'Request failed');
}
},
runAi: async () => {
const { formData } = get();
const res = await query.post({
path: 'ai',
key: 'run',
data: {
key: formData.key,
inputs: [
{
key: 'title',
value: '根据描述生成代码',
},
{
key: 'description',
value: '我想获取一个card, 包含标题和内容标题是evision内容是这是一个测试',
},
],
data: {
title: formData.title,
description: formData.description,
},
},
});
if (res.code === 200) {
console.log(res.data);
message.success('Success');
}
},
};
});

11
src/utils/extra.ts Normal file
View File

@ -0,0 +1,11 @@
export function extractKeysFromBraces(text: string) {
const regex = /\{\{\s*(.*?)\s*\}\}/g;
const keys: string[] = [];
let matches: RegExpExecArray | null;
while ((matches = regex.exec(text)) !== null) {
keys.push(matches[1]); // 获取{{}}中间的key
}
return keys;
}