generated from nhcarrigan/template
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c705b1385a
|
|||
|
5c65eb9ae5
|
|||
| 4776f00e51 | |||
| 9f45ee329d | |||
| 5bfd25e60d | |||
| 662d6119fa |
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tatsumi",
|
"name": "tatsumi",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"@types/react": "19.1.2",
|
"@types/react": "19.1.2",
|
||||||
"@types/react-dom": "19.1.2",
|
"@types/react-dom": "19.1.2",
|
||||||
"autoprefixer": "10.4.21",
|
"autoprefixer": "10.4.21",
|
||||||
"eslint": "10.1.0",
|
"eslint": "9.25.1",
|
||||||
"postcss": "8.5.3",
|
"postcss": "8.5.3",
|
||||||
"tailwindcss": "3.4.17",
|
"tailwindcss": "3.4.17",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.8.3",
|
||||||
|
|||||||
Generated
+185
-126
@@ -26,7 +26,7 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@nhcarrigan/eslint-config':
|
'@nhcarrigan/eslint-config':
|
||||||
specifier: 5.2.0
|
specifier: 5.2.0
|
||||||
version: 5.2.0(@typescript-eslint/utils@8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))(playwright@1.59.1)(react@19.1.0)(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))
|
version: 5.2.0(@typescript-eslint/utils@8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))(playwright@1.59.1)(react@19.1.0)(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))
|
||||||
'@nhcarrigan/typescript-config':
|
'@nhcarrigan/typescript-config':
|
||||||
specifier: 4.0.0
|
specifier: 4.0.0
|
||||||
version: 4.0.0(typescript@5.8.3)
|
version: 4.0.0(typescript@5.8.3)
|
||||||
@@ -46,8 +46,8 @@ importers:
|
|||||||
specifier: 10.4.21
|
specifier: 10.4.21
|
||||||
version: 10.4.21(postcss@8.5.3)
|
version: 10.4.21(postcss@8.5.3)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: 10.1.0
|
specifier: 9.25.1
|
||||||
version: 10.1.0(jiti@1.21.7)
|
version: 9.25.1(jiti@1.21.7)
|
||||||
postcss:
|
postcss:
|
||||||
specifier: 8.5.3
|
specifier: 8.5.3
|
||||||
version: 8.5.3
|
version: 8.5.3
|
||||||
@@ -335,33 +335,41 @@ packages:
|
|||||||
eslint:
|
eslint:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@eslint/config-array@0.23.5':
|
'@eslint/config-array@0.20.1':
|
||||||
resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==}
|
resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/config-helpers@0.5.5':
|
'@eslint/config-helpers@0.2.3':
|
||||||
resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==}
|
resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/core@1.2.1':
|
'@eslint/core@0.13.0':
|
||||||
resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
|
resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/eslintrc@3.2.0':
|
'@eslint/eslintrc@3.2.0':
|
||||||
resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==}
|
resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@eslint/eslintrc@3.3.5':
|
||||||
|
resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==}
|
||||||
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/js@9.17.0':
|
'@eslint/js@9.17.0':
|
||||||
resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==}
|
resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/object-schema@3.0.5':
|
'@eslint/js@9.25.1':
|
||||||
resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
|
resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@eslint/plugin-kit@0.6.1':
|
'@eslint/object-schema@2.1.7':
|
||||||
resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==}
|
resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@eslint/plugin-kit@0.2.8':
|
||||||
|
resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
|
||||||
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@humanfs/core@0.19.1':
|
'@humanfs/core@0.19.1':
|
||||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||||
@@ -665,9 +673,6 @@ packages:
|
|||||||
'@types/deep-eql@4.0.2':
|
'@types/deep-eql@4.0.2':
|
||||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||||
|
|
||||||
'@types/esrecurse@4.3.1':
|
|
||||||
resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
|
|
||||||
|
|
||||||
'@types/estree@1.0.8':
|
'@types/estree@1.0.8':
|
||||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||||
|
|
||||||
@@ -865,6 +870,10 @@ packages:
|
|||||||
ajv@6.14.0:
|
ajv@6.14.0:
|
||||||
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
|
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
any-promise@1.3.0:
|
any-promise@1.3.0:
|
||||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||||
|
|
||||||
@@ -1003,6 +1012,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
@@ -1015,6 +1028,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
|
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
color-convert@2.0.1:
|
||||||
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
|
engines: {node: '>=7.0.0'}
|
||||||
|
|
||||||
|
color-name@1.1.4:
|
||||||
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
commander@4.1.1:
|
commander@4.1.1:
|
||||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -1231,9 +1251,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=8.56.0'
|
eslint: '>=8.56.0'
|
||||||
|
|
||||||
eslint-scope@9.1.2:
|
eslint-scope@8.4.0:
|
||||||
resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
|
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
eslint-visitor-keys@1.3.0:
|
eslint-visitor-keys@1.3.0:
|
||||||
resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
|
resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
|
||||||
@@ -1251,9 +1271,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
|
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||||
|
|
||||||
eslint@10.1.0:
|
eslint@9.25.1:
|
||||||
resolution: {integrity: sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==}
|
resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==}
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
jiti: '*'
|
jiti: '*'
|
||||||
@@ -1265,10 +1285,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
|
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
espree@11.2.0:
|
|
||||||
resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
|
|
||||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
|
||||||
|
|
||||||
espree@6.2.1:
|
espree@6.2.1:
|
||||||
resolution: {integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==}
|
resolution: {integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
@@ -1430,6 +1446,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
has-flag@4.0.0:
|
||||||
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
has-property-descriptors@1.0.2:
|
has-property-descriptors@1.0.2:
|
||||||
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
|
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
|
||||||
|
|
||||||
@@ -1664,6 +1684,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
lodash.merge@4.6.2:
|
||||||
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
loose-envify@1.4.0:
|
loose-envify@1.4.0:
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -2141,6 +2164,10 @@ packages:
|
|||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
supports-preserve-symlinks-flag@1.0.0:
|
supports-preserve-symlinks-flag@1.0.0:
|
||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2580,36 +2607,34 @@ snapshots:
|
|||||||
'@esbuild/win32-x64@0.25.12':
|
'@esbuild/win32-x64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@eslint-community/eslint-plugin-eslint-comments@4.4.1(eslint@10.1.0(jiti@1.21.7))':
|
'@eslint-community/eslint-plugin-eslint-comments@4.4.1(eslint@9.25.1(jiti@1.21.7))':
|
||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
ignore: 5.3.2
|
ignore: 5.3.2
|
||||||
|
|
||||||
'@eslint-community/eslint-utils@4.9.1(eslint@10.1.0(jiti@1.21.7))':
|
'@eslint-community/eslint-utils@4.9.1(eslint@9.25.1(jiti@1.21.7))':
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
eslint-visitor-keys: 3.4.3
|
eslint-visitor-keys: 3.4.3
|
||||||
|
|
||||||
'@eslint-community/regexpp@4.12.2': {}
|
'@eslint-community/regexpp@4.12.2': {}
|
||||||
|
|
||||||
'@eslint/compat@1.2.4(eslint@10.1.0(jiti@1.21.7))':
|
'@eslint/compat@1.2.4(eslint@9.25.1(jiti@1.21.7))':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
|
|
||||||
'@eslint/config-array@0.23.5':
|
'@eslint/config-array@0.20.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint/object-schema': 3.0.5
|
'@eslint/object-schema': 2.1.7
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
minimatch: 10.2.5
|
minimatch: 3.1.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@eslint/config-helpers@0.5.5':
|
'@eslint/config-helpers@0.2.3': {}
|
||||||
dependencies:
|
|
||||||
'@eslint/core': 1.2.1
|
|
||||||
|
|
||||||
'@eslint/core@1.2.1':
|
'@eslint/core@0.13.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/json-schema': 7.0.15
|
'@types/json-schema': 7.0.15
|
||||||
|
|
||||||
@@ -2627,13 +2652,29 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@eslint/eslintrc@3.3.5':
|
||||||
|
dependencies:
|
||||||
|
ajv: 6.14.0
|
||||||
|
debug: 4.4.3
|
||||||
|
espree: 10.4.0
|
||||||
|
globals: 14.0.0
|
||||||
|
ignore: 5.3.2
|
||||||
|
import-fresh: 3.3.1
|
||||||
|
js-yaml: 4.1.1
|
||||||
|
minimatch: 3.1.5
|
||||||
|
strip-json-comments: 3.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@eslint/js@9.17.0': {}
|
'@eslint/js@9.17.0': {}
|
||||||
|
|
||||||
'@eslint/object-schema@3.0.5': {}
|
'@eslint/js@9.25.1': {}
|
||||||
|
|
||||||
'@eslint/plugin-kit@0.6.1':
|
'@eslint/object-schema@2.1.7': {}
|
||||||
|
|
||||||
|
'@eslint/plugin-kit@0.2.8':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint/core': 1.2.1
|
'@eslint/core': 0.13.0
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
@@ -2666,24 +2707,24 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
'@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))(playwright@1.59.1)(react@19.1.0)(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))':
|
'@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))(playwright@1.59.1)(react@19.1.0)(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@10.1.0(jiti@1.21.7))
|
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
'@eslint/compat': 1.2.4(eslint@10.1.0(jiti@1.21.7))
|
'@eslint/compat': 1.2.4(eslint@9.25.1(jiti@1.21.7))
|
||||||
'@eslint/eslintrc': 3.2.0
|
'@eslint/eslintrc': 3.2.0
|
||||||
'@eslint/js': 9.17.0
|
'@eslint/js': 9.17.0
|
||||||
'@stylistic/eslint-plugin': 2.12.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@stylistic/eslint-plugin': 2.12.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
'@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
'@typescript-eslint/parser': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
'@vitest/eslint-plugin': 1.1.24(@typescript-eslint/utils@8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))
|
'@vitest/eslint-plugin': 1.1.24(@typescript-eslint/utils@8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
eslint-plugin-deprecation: 3.0.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
eslint-plugin-deprecation: 3.0.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))
|
||||||
eslint-plugin-jsdoc: 50.6.1(eslint@10.1.0(jiti@1.21.7))
|
eslint-plugin-jsdoc: 50.6.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
eslint-plugin-playwright: 2.1.0(eslint@10.1.0(jiti@1.21.7))
|
eslint-plugin-playwright: 2.1.0(eslint@9.25.1(jiti@1.21.7))
|
||||||
eslint-plugin-react: 7.37.3(eslint@10.1.0(jiti@1.21.7))
|
eslint-plugin-react: 7.37.3(eslint@9.25.1(jiti@1.21.7))
|
||||||
eslint-plugin-sort-keys-fix: 1.1.2
|
eslint-plugin-sort-keys-fix: 1.1.2
|
||||||
eslint-plugin-unicorn: 56.0.1(eslint@10.1.0(jiti@1.21.7))
|
eslint-plugin-unicorn: 56.0.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
globals: 15.14.0
|
globals: 15.14.0
|
||||||
playwright: 1.59.1
|
playwright: 1.59.1
|
||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
@@ -2792,10 +2833,10 @@ snapshots:
|
|||||||
|
|
||||||
'@standard-schema/spec@1.1.0': {}
|
'@standard-schema/spec@1.1.0': {}
|
||||||
|
|
||||||
'@stylistic/eslint-plugin@2.12.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@stylistic/eslint-plugin@2.12.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/utils': 8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
espree: 10.4.0
|
espree: 10.4.0
|
||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
@@ -2891,8 +2932,6 @@ snapshots:
|
|||||||
|
|
||||||
'@types/deep-eql@4.0.2': {}
|
'@types/deep-eql@4.0.2': {}
|
||||||
|
|
||||||
'@types/esrecurse@4.3.1': {}
|
|
||||||
|
|
||||||
'@types/estree@1.0.8': {}
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
'@types/json-schema@7.0.15': {}
|
'@types/json-schema@7.0.15': {}
|
||||||
@@ -2909,15 +2948,15 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
csstype: 3.2.3
|
csstype: 3.2.3
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
'@typescript-eslint/parser': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
'@typescript-eslint/scope-manager': 8.19.0
|
'@typescript-eslint/scope-manager': 8.19.0
|
||||||
'@typescript-eslint/type-utils': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/type-utils': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
'@typescript-eslint/utils': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.19.0
|
'@typescript-eslint/visitor-keys': 8.19.0
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
graphemer: 1.4.0
|
graphemer: 1.4.0
|
||||||
ignore: 5.3.2
|
ignore: 5.3.2
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
@@ -2926,14 +2965,14 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.19.0
|
'@typescript-eslint/scope-manager': 8.19.0
|
||||||
'@typescript-eslint/types': 8.19.0
|
'@typescript-eslint/types': 8.19.0
|
||||||
'@typescript-eslint/typescript-estree': 8.19.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.19.0(typescript@5.8.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.19.0
|
'@typescript-eslint/visitor-keys': 8.19.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -2966,12 +3005,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@typescript-eslint/type-utils@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 8.19.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.19.0(typescript@5.8.3)
|
||||||
'@typescript-eslint/utils': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
ts-api-utils: 1.4.3(typescript@5.8.3)
|
ts-api-utils: 1.4.3(typescript@5.8.3)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -3027,35 +3066,35 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/utils@7.18.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@typescript-eslint/utils@7.18.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@1.21.7))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
'@typescript-eslint/scope-manager': 7.18.0
|
'@typescript-eslint/scope-manager': 7.18.0
|
||||||
'@typescript-eslint/types': 7.18.0
|
'@typescript-eslint/types': 7.18.0
|
||||||
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@typescript-eslint/utils@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@1.21.7))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
'@typescript-eslint/scope-manager': 8.19.0
|
'@typescript-eslint/scope-manager': 8.19.0
|
||||||
'@typescript-eslint/types': 8.19.0
|
'@typescript-eslint/types': 8.19.0
|
||||||
'@typescript-eslint/typescript-estree': 8.19.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.19.0(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)':
|
'@typescript-eslint/utils@8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@1.21.7))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
'@typescript-eslint/scope-manager': 8.58.1
|
'@typescript-eslint/scope-manager': 8.58.1
|
||||||
'@typescript-eslint/types': 8.58.1
|
'@typescript-eslint/types': 8.58.1
|
||||||
'@typescript-eslint/typescript-estree': 8.58.1(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.58.1(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -3086,10 +3125,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@vitest/eslint-plugin@1.1.24(@typescript-eslint/utils@8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))':
|
'@vitest/eslint-plugin@1.1.24(@typescript-eslint/utils@8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)(vitest@4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/utils': 8.58.1(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.58.1(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
vitest: 4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3))
|
vitest: 4.1.4(vite@6.3.2(jiti@1.21.7)(yaml@2.8.3))
|
||||||
@@ -3154,6 +3193,10 @@ snapshots:
|
|||||||
json-schema-traverse: 0.4.1
|
json-schema-traverse: 0.4.1
|
||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
dependencies:
|
||||||
|
color-convert: 2.0.1
|
||||||
|
|
||||||
any-promise@1.3.0: {}
|
any-promise@1.3.0: {}
|
||||||
|
|
||||||
anymatch@3.1.3:
|
anymatch@3.1.3:
|
||||||
@@ -3314,6 +3357,11 @@ snapshots:
|
|||||||
|
|
||||||
chai@6.2.2: {}
|
chai@6.2.2: {}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
supports-color: 7.2.0
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch: 3.1.3
|
anymatch: 3.1.3
|
||||||
@@ -3332,6 +3380,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp: 1.0.5
|
escape-string-regexp: 1.0.5
|
||||||
|
|
||||||
|
color-convert@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
color-name: 1.1.4
|
||||||
|
|
||||||
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
commander@4.1.1: {}
|
commander@4.1.1: {}
|
||||||
|
|
||||||
comment-parser@1.4.1: {}
|
comment-parser@1.4.1: {}
|
||||||
@@ -3567,27 +3621,27 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint@10.1.0(jiti@1.21.7)):
|
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint@9.25.1(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
eslint-import-resolver-node: 0.3.10
|
eslint-import-resolver-node: 0.3.10
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-deprecation@3.0.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3):
|
eslint-plugin-deprecation@3.0.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/utils': 7.18.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/utils': 7.18.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
ts-api-utils: 1.4.3(typescript@5.8.3)
|
ts-api-utils: 1.4.3(typescript@5.8.3)
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint@10.1.0(jiti@1.21.7)):
|
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.25.1(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.9
|
array-includes: 3.1.9
|
||||||
@@ -3596,9 +3650,9 @@ snapshots:
|
|||||||
array.prototype.flatmap: 1.3.3
|
array.prototype.flatmap: 1.3.3
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
eslint-import-resolver-node: 0.3.10
|
eslint-import-resolver-node: 0.3.10
|
||||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint@10.1.0(jiti@1.21.7))
|
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-node@0.3.10)(eslint@9.25.1(jiti@1.21.7))
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.16.1
|
is-core-module: 2.16.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -3610,20 +3664,20 @@ snapshots:
|
|||||||
string.prototype.trimend: 1.0.9
|
string.prototype.trimend: 1.0.9
|
||||||
tsconfig-paths: 3.15.0
|
tsconfig-paths: 3.15.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.19.0(eslint@10.1.0(jiti@1.21.7))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.19.0(eslint@9.25.1(jiti@1.21.7))(typescript@5.8.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- eslint-import-resolver-typescript
|
- eslint-import-resolver-typescript
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-jsdoc@50.6.1(eslint@10.1.0(jiti@1.21.7)):
|
eslint-plugin-jsdoc@50.6.1(eslint@9.25.1(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@es-joy/jsdoccomment': 0.49.0
|
'@es-joy/jsdoccomment': 0.49.0
|
||||||
are-docs-informative: 0.0.2
|
are-docs-informative: 0.0.2
|
||||||
comment-parser: 1.4.1
|
comment-parser: 1.4.1
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
espree: 10.4.0
|
espree: 10.4.0
|
||||||
esquery: 1.7.0
|
esquery: 1.7.0
|
||||||
parse-imports: 2.2.1
|
parse-imports: 2.2.1
|
||||||
@@ -3633,12 +3687,12 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-playwright@2.1.0(eslint@10.1.0(jiti@1.21.7)):
|
eslint-plugin-playwright@2.1.0(eslint@9.25.1(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
globals: 13.24.0
|
globals: 13.24.0
|
||||||
|
|
||||||
eslint-plugin-react@7.37.3(eslint@10.1.0(jiti@1.21.7)):
|
eslint-plugin-react@7.37.3(eslint@9.25.1(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
array-includes: 3.1.9
|
array-includes: 3.1.9
|
||||||
array.prototype.findlast: 1.2.5
|
array.prototype.findlast: 1.2.5
|
||||||
@@ -3646,7 +3700,7 @@ snapshots:
|
|||||||
array.prototype.tosorted: 1.1.4
|
array.prototype.tosorted: 1.1.4
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
es-iterator-helpers: 1.3.1
|
es-iterator-helpers: 1.3.1
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
jsx-ast-utils: 3.3.5
|
jsx-ast-utils: 3.3.5
|
||||||
@@ -3667,14 +3721,14 @@ snapshots:
|
|||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
requireindex: 1.2.0
|
requireindex: 1.2.0
|
||||||
|
|
||||||
eslint-plugin-unicorn@56.0.1(eslint@10.1.0(jiti@1.21.7)):
|
eslint-plugin-unicorn@56.0.1(eslint@9.25.1(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.28.5
|
'@babel/helper-validator-identifier': 7.28.5
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@1.21.7))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
ci-info: 4.4.0
|
ci-info: 4.4.0
|
||||||
clean-regexp: 1.0.0
|
clean-regexp: 1.0.0
|
||||||
core-js-compat: 3.49.0
|
core-js-compat: 3.49.0
|
||||||
eslint: 10.1.0(jiti@1.21.7)
|
eslint: 9.25.1(jiti@1.21.7)
|
||||||
esquery: 1.7.0
|
esquery: 1.7.0
|
||||||
globals: 15.14.0
|
globals: 15.14.0
|
||||||
indent-string: 4.0.0
|
indent-string: 4.0.0
|
||||||
@@ -3687,10 +3741,8 @@ snapshots:
|
|||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
strip-indent: 3.0.0
|
strip-indent: 3.0.0
|
||||||
|
|
||||||
eslint-scope@9.1.2:
|
eslint-scope@8.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/esrecurse': 4.3.1
|
|
||||||
'@types/estree': 1.0.8
|
|
||||||
esrecurse: 4.3.0
|
esrecurse: 4.3.0
|
||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
|
|
||||||
@@ -3702,25 +3754,29 @@ snapshots:
|
|||||||
|
|
||||||
eslint-visitor-keys@5.0.1: {}
|
eslint-visitor-keys@5.0.1: {}
|
||||||
|
|
||||||
eslint@10.1.0(jiti@1.21.7):
|
eslint@9.25.1(jiti@1.21.7):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@1.21.7))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1(jiti@1.21.7))
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
'@eslint/config-array': 0.23.5
|
'@eslint/config-array': 0.20.1
|
||||||
'@eslint/config-helpers': 0.5.5
|
'@eslint/config-helpers': 0.2.3
|
||||||
'@eslint/core': 1.2.1
|
'@eslint/core': 0.13.0
|
||||||
'@eslint/plugin-kit': 0.6.1
|
'@eslint/eslintrc': 3.3.5
|
||||||
|
'@eslint/js': 9.25.1
|
||||||
|
'@eslint/plugin-kit': 0.2.8
|
||||||
'@humanfs/node': 0.16.7
|
'@humanfs/node': 0.16.7
|
||||||
'@humanwhocodes/module-importer': 1.0.1
|
'@humanwhocodes/module-importer': 1.0.1
|
||||||
'@humanwhocodes/retry': 0.4.3
|
'@humanwhocodes/retry': 0.4.3
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
ajv: 6.14.0
|
ajv: 6.14.0
|
||||||
|
chalk: 4.1.2
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 9.1.2
|
eslint-scope: 8.4.0
|
||||||
eslint-visitor-keys: 5.0.1
|
eslint-visitor-keys: 4.2.1
|
||||||
espree: 11.2.0
|
espree: 10.4.0
|
||||||
esquery: 1.7.0
|
esquery: 1.7.0
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
@@ -3731,7 +3787,8 @@ snapshots:
|
|||||||
imurmurhash: 0.1.4
|
imurmurhash: 0.1.4
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
json-stable-stringify-without-jsonify: 1.0.1
|
json-stable-stringify-without-jsonify: 1.0.1
|
||||||
minimatch: 10.2.5
|
lodash.merge: 4.6.2
|
||||||
|
minimatch: 3.1.5
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
optionator: 0.9.4
|
optionator: 0.9.4
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -3745,12 +3802,6 @@ snapshots:
|
|||||||
acorn-jsx: 5.3.2(acorn@8.16.0)
|
acorn-jsx: 5.3.2(acorn@8.16.0)
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
espree@11.2.0:
|
|
||||||
dependencies:
|
|
||||||
acorn: 8.16.0
|
|
||||||
acorn-jsx: 5.3.2(acorn@8.16.0)
|
|
||||||
eslint-visitor-keys: 5.0.1
|
|
||||||
|
|
||||||
espree@6.2.1:
|
espree@6.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 7.4.1
|
acorn: 7.4.1
|
||||||
@@ -3911,6 +3962,8 @@ snapshots:
|
|||||||
|
|
||||||
has-bigints@1.1.0: {}
|
has-bigints@1.1.0: {}
|
||||||
|
|
||||||
|
has-flag@4.0.0: {}
|
||||||
|
|
||||||
has-property-descriptors@1.0.2:
|
has-property-descriptors@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-define-property: 1.0.1
|
es-define-property: 1.0.1
|
||||||
@@ -4135,6 +4188,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
||||||
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
loose-envify@1.4.0:
|
loose-envify@1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
@@ -4675,6 +4730,10 @@ snapshots:
|
|||||||
tinyglobby: 0.2.16
|
tinyglobby: 0.2.16
|
||||||
ts-interface-checker: 0.1.13
|
ts-interface-checker: 0.1.13
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
|
||||||
supports-preserve-symlinks-flag@1.0.0: {}
|
supports-preserve-symlinks-flag@1.0.0: {}
|
||||||
|
|
||||||
synckit@0.9.3:
|
synckit@0.9.3:
|
||||||
|
|||||||
Generated
+1
-1
@@ -3716,7 +3716,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tatsumi"
|
name = "tatsumi"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tatsumi"
|
name = "tatsumi"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
description = "Tatsumi - AI art generation using Google Gemini"
|
description = "Tatsumi - AI art generation using Google Gemini"
|
||||||
authors = ["Naomi Carrigan"]
|
authors = ["Naomi Carrigan"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 298 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 MiB After Width: | Height: | Size: 17 MiB |
+58
-23
@@ -11,11 +11,11 @@ Character design (always required):
|
|||||||
- Wavy ashen brown hair (colour and texture fixed; hairstyle can vary)
|
- Wavy ashen brown hair (colour and texture fixed; hairstyle can vary)
|
||||||
- Very pale skin tone
|
- Very pale skin tone
|
||||||
- Vibrant sky-blue eyes — important, commonly missed
|
- Vibrant sky-blue eyes — important, commonly missed
|
||||||
- Vampire fangs
|
- Vampire fangs (only visible when mouth is open)
|
||||||
- Glasses (pink-framed preferred, other styles acceptable)
|
- Glasses (pink-framed preferred, other styles acceptable)
|
||||||
- Painted fingernails and toenails (any colour, never unpolished)
|
- Painted fingernails and toenails (any colour, never unpolished)
|
||||||
- Slender build
|
- Slender build
|
||||||
- Full body visible in frame; always barefoot, never wears socks
|
- Full body visible in frame
|
||||||
|
|
||||||
Composition (always required):
|
Composition (always required):
|
||||||
- Single character only
|
- Single character only
|
||||||
@@ -46,12 +46,19 @@ fn build_safety_settings() -> Value {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_generation_config(mode: &str) -> Value {
|
fn build_generation_config(
|
||||||
|
mode: &str,
|
||||||
|
aspect_ratio: Option<&str>,
|
||||||
|
image_size: &str,
|
||||||
|
) -> Value {
|
||||||
let image_config = match mode {
|
let image_config = match mode {
|
||||||
"avatar" => json!({ "aspectRatio": "1:1", "imageSize": "4K" }),
|
"avatar" => json!({ "aspectRatio": "1:1", "imageSize": image_size }),
|
||||||
"art" => json!({ "aspectRatio": "16:9", "imageSize": "4K" }),
|
"art" => {
|
||||||
|
let ratio = aspect_ratio.unwrap_or("16:9");
|
||||||
|
json!({ "aspectRatio": ratio, "imageSize": image_size })
|
||||||
|
}
|
||||||
// replace mode: omit aspectRatio so the model infers it from the source image
|
// replace mode: omit aspectRatio so the model infers it from the source image
|
||||||
_ => json!({ "imageSize": "4K" }),
|
_ => json!({ "imageSize": image_size }),
|
||||||
};
|
};
|
||||||
json!({
|
json!({
|
||||||
"imageConfig": image_config,
|
"imageConfig": image_config,
|
||||||
@@ -83,37 +90,56 @@ fn build_user_gemini_parts(
|
|||||||
user_image_base64: &Option<String>,
|
user_image_base64: &Option<String>,
|
||||||
user_image_mime: &Option<String>,
|
user_image_mime: &Option<String>,
|
||||||
) -> Vec<Value> {
|
) -> Vec<Value> {
|
||||||
if mode == "replace" && user_image_base64.is_some() {
|
if let Some(image_data) = user_image_base64.as_deref() {
|
||||||
let mime = user_image_mime.as_deref().unwrap_or("image/png");
|
let mime = user_image_mime.as_deref().unwrap_or("image/png");
|
||||||
let data = user_image_base64.as_deref().unwrap_or("");
|
|
||||||
let base_text = user_text.as_deref().unwrap_or("");
|
let base_text = user_text.as_deref().unwrap_or("");
|
||||||
let final_text = if base_text.is_empty() {
|
let final_text = if mode == "replace" {
|
||||||
REPLACE_MODE_APPEND.to_string()
|
if base_text.is_empty() {
|
||||||
|
REPLACE_MODE_APPEND.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}\n{}", base_text, REPLACE_MODE_APPEND)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{}\n{}", base_text, REPLACE_MODE_APPEND)
|
base_text.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
let mut parts = vec![json!({"inlineData": {"mimeType": mime, "data": image_data}})];
|
||||||
json!({"inlineData": {"mimeType": mime, "data": data}}),
|
if !final_text.is_empty() {
|
||||||
json!({"text": final_text}),
|
parts.push(json!({"text": final_text}));
|
||||||
]
|
}
|
||||||
|
parts
|
||||||
} else {
|
} else {
|
||||||
// Art/avatar mode, or replace mode follow-up correction (text only)
|
// No image: text-only message
|
||||||
let text = user_text.as_deref().unwrap_or("");
|
let text = user_text.as_deref().unwrap_or("");
|
||||||
vec![json!({"text": text})]
|
vec![json!({"text": text})]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GeminiCallParams {
|
||||||
|
pub mode: String,
|
||||||
|
pub aspect_ratio: Option<String>,
|
||||||
|
pub image_size: String,
|
||||||
|
pub user_text: Option<String>,
|
||||||
|
pub user_image_base64: Option<String>,
|
||||||
|
pub user_image_mime: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn call_gemini(
|
pub async fn call_gemini(
|
||||||
api_key: String,
|
api_key: String,
|
||||||
mode: String,
|
|
||||||
history: Vec<ThreadMessage>,
|
history: Vec<ThreadMessage>,
|
||||||
user_text: Option<String>,
|
params: GeminiCallParams,
|
||||||
user_image_base64: Option<String>,
|
|
||||||
user_image_mime: Option<String>,
|
|
||||||
) -> Result<(Vec<MessagePart>, f64), String> {
|
) -> Result<(Vec<MessagePart>, f64), String> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let GeminiCallParams {
|
||||||
|
mode,
|
||||||
|
aspect_ratio,
|
||||||
|
image_size,
|
||||||
|
user_text,
|
||||||
|
user_image_base64,
|
||||||
|
user_image_mime,
|
||||||
|
} = params;
|
||||||
|
|
||||||
let is_first_message = history.is_empty();
|
let is_first_message = history.is_empty();
|
||||||
|
|
||||||
let mut contents: Vec<Value> = history
|
let mut contents: Vec<Value> = history
|
||||||
@@ -156,7 +182,11 @@ pub async fn call_gemini(
|
|||||||
|
|
||||||
contents.push(json!({"role": "user", "parts": user_parts}));
|
contents.push(json!({"role": "user", "parts": user_parts}));
|
||||||
|
|
||||||
let generation_config = build_generation_config(mode.as_str());
|
let generation_config = build_generation_config(
|
||||||
|
mode.as_str(),
|
||||||
|
aspect_ratio.as_deref(),
|
||||||
|
image_size.as_str(),
|
||||||
|
);
|
||||||
let safety_settings = build_safety_settings();
|
let safety_settings = build_safety_settings();
|
||||||
|
|
||||||
let request_body = json!({
|
let request_body = json!({
|
||||||
@@ -248,8 +278,13 @@ pub async fn call_gemini(
|
|||||||
let candidates_tokens = usage["candidatesTokenCount"].as_u64().unwrap_or(0);
|
let candidates_tokens = usage["candidatesTokenCount"].as_u64().unwrap_or(0);
|
||||||
let image_part_count = result_parts.iter().filter(|p| p.part_type == "image").count() as u64;
|
let image_part_count = result_parts.iter().filter(|p| p.part_type == "image").count() as u64;
|
||||||
|
|
||||||
// Image output tokens (4K = 2000 tokens each) billed at $120/1M tokens
|
// Image output tokens per image vary by resolution, billed at $120/1M tokens
|
||||||
let image_output_tokens = image_part_count * 2_000_u64;
|
let tokens_per_image: u64 = match image_size.as_str() {
|
||||||
|
"1K" => 500,
|
||||||
|
"2K" => 1_000,
|
||||||
|
_ => 2_000, // 4K default
|
||||||
|
};
|
||||||
|
let image_output_tokens = image_part_count * tokens_per_image;
|
||||||
// Remaining candidates tokens are text/thinking, billed at $12/1M tokens
|
// Remaining candidates tokens are text/thinking, billed at $12/1M tokens
|
||||||
let text_output_tokens = candidates_tokens.saturating_sub(image_output_tokens);
|
let text_output_tokens = candidates_tokens.saturating_sub(image_output_tokens);
|
||||||
|
|
||||||
|
|||||||
+17
-3
@@ -1,7 +1,7 @@
|
|||||||
mod gemini;
|
mod gemini;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
use gemini::{call_gemini, read_reference_image_base64};
|
use gemini::{call_gemini, read_reference_image_base64, GeminiCallParams};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use storage::{
|
use storage::{
|
||||||
delete_thread_from_disk, load_config_from_disk, load_threads_from_disk, save_config_to_disk,
|
delete_thread_from_disk, load_config_from_disk, load_threads_from_disk, save_config_to_disk,
|
||||||
@@ -46,16 +46,30 @@ async fn save_config(config: Config) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn send_message(
|
async fn send_message(
|
||||||
api_key: String,
|
api_key: String,
|
||||||
mode: String,
|
mode: String,
|
||||||
|
aspect_ratio: Option<String>,
|
||||||
|
image_size: String,
|
||||||
history: Vec<ThreadMessage>,
|
history: Vec<ThreadMessage>,
|
||||||
user_text: Option<String>,
|
user_text: Option<String>,
|
||||||
user_image_base64: Option<String>,
|
user_image_base64: Option<String>,
|
||||||
user_image_mime: Option<String>,
|
user_image_mime: Option<String>,
|
||||||
) -> Result<SendMessageResult, String> {
|
) -> Result<SendMessageResult, String> {
|
||||||
let (parts, cost_usd) =
|
let (parts, cost_usd) = call_gemini(
|
||||||
call_gemini(api_key, mode, history, user_text, user_image_base64, user_image_mime).await?;
|
api_key,
|
||||||
|
history,
|
||||||
|
GeminiCallParams {
|
||||||
|
mode,
|
||||||
|
aspect_ratio,
|
||||||
|
image_size,
|
||||||
|
user_text,
|
||||||
|
user_image_base64,
|
||||||
|
user_image_mime,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(SendMessageResult { parts, cost_usd })
|
Ok(SendMessageResult { parts, cost_usd })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,19 @@ pub struct ThreadMessage {
|
|||||||
pub cost_usd: Option<f64>,
|
pub cost_usd: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_image_size() -> String {
|
||||||
|
"4K".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub mode: String,
|
pub mode: String,
|
||||||
|
#[serde(rename = "aspectRatio", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub aspect_ratio: Option<String>,
|
||||||
|
#[serde(rename = "imageSize", default = "default_image_size")]
|
||||||
|
pub image_size: String,
|
||||||
pub messages: Vec<ThreadMessage>,
|
pub messages: Vec<ThreadMessage>,
|
||||||
#[serde(rename = "createdAt")]
|
#[serde(rename = "createdAt")]
|
||||||
pub created_at: i64,
|
pub created_at: i64,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "tatsumi",
|
"productName": "tatsumi",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"identifier": "com.naomi.tatsumi",
|
"identifier": "com.naomi.tatsumi",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
+129
-18
@@ -16,17 +16,127 @@ import { SettingsModal } from "./components/settingsModal.tsx";
|
|||||||
import { Sidebar } from "./components/sidebar.tsx";
|
import { Sidebar } from "./components/sidebar.tsx";
|
||||||
import { ThreadView } from "./components/threadView.tsx";
|
import { ThreadView } from "./components/threadView.tsx";
|
||||||
import { WelcomeScreen } from "./components/welcomeScreen.tsx";
|
import { WelcomeScreen } from "./components/welcomeScreen.tsx";
|
||||||
import type { Config, Mode, PendingInput, Thread } from "./types/index.ts";
|
import type {
|
||||||
|
AspectRatio,
|
||||||
|
Config,
|
||||||
|
ImageSize,
|
||||||
|
Mode,
|
||||||
|
PendingInput,
|
||||||
|
Thread,
|
||||||
|
} from "./types/index.ts";
|
||||||
|
|
||||||
const generateThreadName = (mode: Mode, text?: string): string => {
|
const adjectives = [
|
||||||
if (
|
"Amber",
|
||||||
mode === "replace"
|
"Ancient",
|
||||||
|| text === undefined
|
"Arctic",
|
||||||
|| text.trim().length === 0
|
"Azure",
|
||||||
) {
|
"Blazing",
|
||||||
return `Replace - ${new Date().toLocaleString()}`;
|
"Celestial",
|
||||||
}
|
"Cobalt",
|
||||||
return text.trim().slice(0, 40);
|
"Crimson",
|
||||||
|
"Crystal",
|
||||||
|
"Dark",
|
||||||
|
"Distant",
|
||||||
|
"Ember",
|
||||||
|
"Emerald",
|
||||||
|
"Eternal",
|
||||||
|
"Ethereal",
|
||||||
|
"Fleeting",
|
||||||
|
"Frosted",
|
||||||
|
"Gilded",
|
||||||
|
"Golden",
|
||||||
|
"Hidden",
|
||||||
|
"Hollow",
|
||||||
|
"Indigo",
|
||||||
|
"Ivory",
|
||||||
|
"Jade",
|
||||||
|
"Lunar",
|
||||||
|
"Midnight",
|
||||||
|
"Mystic",
|
||||||
|
"Neon",
|
||||||
|
"Obsidian",
|
||||||
|
"Onyx",
|
||||||
|
"Opal",
|
||||||
|
"Pale",
|
||||||
|
"Pearl",
|
||||||
|
"Radiant",
|
||||||
|
"Rusted",
|
||||||
|
"Sapphire",
|
||||||
|
"Scarlet",
|
||||||
|
"Shattered",
|
||||||
|
"Silent",
|
||||||
|
"Silver",
|
||||||
|
"Spectral",
|
||||||
|
"Twilight",
|
||||||
|
"Velvet",
|
||||||
|
"Verdant",
|
||||||
|
"Veiled",
|
||||||
|
"Violet",
|
||||||
|
"Wandering",
|
||||||
|
"Wild",
|
||||||
|
"Woven",
|
||||||
|
"Arcane",
|
||||||
|
];
|
||||||
|
|
||||||
|
const nouns = [
|
||||||
|
"Abyss",
|
||||||
|
"Archive",
|
||||||
|
"Aria",
|
||||||
|
"Bloom",
|
||||||
|
"Cascade",
|
||||||
|
"Chronicle",
|
||||||
|
"Cipher",
|
||||||
|
"Compass",
|
||||||
|
"Crown",
|
||||||
|
"Dawn",
|
||||||
|
"Dream",
|
||||||
|
"Echo",
|
||||||
|
"Elegy",
|
||||||
|
"Ember",
|
||||||
|
"Equinox",
|
||||||
|
"Flame",
|
||||||
|
"Fragment",
|
||||||
|
"Garden",
|
||||||
|
"Horizon",
|
||||||
|
"Labyrinth",
|
||||||
|
"Lantern",
|
||||||
|
"Mirage",
|
||||||
|
"Mist",
|
||||||
|
"Murmur",
|
||||||
|
"Nexus",
|
||||||
|
"Nocturne",
|
||||||
|
"Oracle",
|
||||||
|
"Phantom",
|
||||||
|
"Prism",
|
||||||
|
"Requiem",
|
||||||
|
"Reverie",
|
||||||
|
"Ruin",
|
||||||
|
"Shadow",
|
||||||
|
"Shard",
|
||||||
|
"Silence",
|
||||||
|
"Solstice",
|
||||||
|
"Sonata",
|
||||||
|
"Specter",
|
||||||
|
"Storm",
|
||||||
|
"Tempest",
|
||||||
|
"Throne",
|
||||||
|
"Tide",
|
||||||
|
"Veil",
|
||||||
|
"Vortex",
|
||||||
|
"Whisper",
|
||||||
|
"Zenith",
|
||||||
|
"Sigil",
|
||||||
|
"Glyph",
|
||||||
|
"Dusk",
|
||||||
|
"Omen",
|
||||||
|
];
|
||||||
|
|
||||||
|
const generateThreadName = (): string => {
|
||||||
|
const adjectiveIndex = Math.floor(Math.random() * adjectives.length);
|
||||||
|
const nounIndex = Math.floor(Math.random() * nouns.length);
|
||||||
|
const adjective = adjectives[adjectiveIndex] ?? "Arcane";
|
||||||
|
const noun = nouns[nounIndex] ?? "Reverie";
|
||||||
|
return `${adjective} ${noun}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateId = (): string => {
|
const generateId = (): string => {
|
||||||
@@ -67,13 +177,7 @@ const resolveUpdatedThread = (updatedThread: Thread): Thread => {
|
|||||||
return updatedThread;
|
return updatedThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstTextPart = firstMessage.parts.find((part) => {
|
const name = generateThreadName();
|
||||||
return part.type === "text";
|
|
||||||
});
|
|
||||||
const name = generateThreadName(
|
|
||||||
updatedThread.mode,
|
|
||||||
firstTextPart?.text,
|
|
||||||
);
|
|
||||||
return { ...updatedThread, name };
|
return { ...updatedThread, name };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,11 +224,18 @@ const app = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleNewThread = useCallback(
|
const handleNewThread = useCallback(
|
||||||
(mode: Mode): void => {
|
(
|
||||||
|
mode: Mode,
|
||||||
|
aspectRatio: AspectRatio | undefined,
|
||||||
|
imageSize: ImageSize,
|
||||||
|
): void => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const createdThread: Thread = {
|
const createdThread: Thread = {
|
||||||
|
/* eslint-disable-next-line stylistic/no-extra-parens -- Required for conditional spread with exactOptionalPropertyTypes */
|
||||||
|
...(aspectRatio !== undefined && { aspectRatio }),
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
|
imageSize: imageSize,
|
||||||
messages: [],
|
messages: [],
|
||||||
mode: mode,
|
mode: mode,
|
||||||
name: `New ${mode} thread`,
|
name: `New ${mode} thread`,
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ import {
|
|||||||
type JSX,
|
type JSX,
|
||||||
type KeyboardEvent,
|
type KeyboardEvent,
|
||||||
} from "react";
|
} from "react";
|
||||||
import type { Mode, PendingInput } from "../types/index.ts";
|
import type {
|
||||||
|
AspectRatio,
|
||||||
|
ImageSize,
|
||||||
|
Mode,
|
||||||
|
PendingInput,
|
||||||
|
} from "../types/index.ts";
|
||||||
|
|
||||||
const dropZoneBaseClass = [
|
const dropZoneBaseClass = [
|
||||||
"border-2 border-dashed rounded-xl p-8",
|
"border-2 border-dashed rounded-xl p-8",
|
||||||
@@ -113,14 +118,19 @@ const readClipboardImage = async(
|
|||||||
onFileRead(new File([ blob ], "clipboard.png", { type: imageType }));
|
onFileRead(new File([ blob ], "clipboard.png", { type: imageType }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const modeLabelText = (mode: Mode): string => {
|
const modeLabelText = (
|
||||||
|
mode: Mode,
|
||||||
|
aspectRatio: AspectRatio | undefined,
|
||||||
|
imageSize: ImageSize,
|
||||||
|
): string => {
|
||||||
if (mode === "avatar") {
|
if (mode === "avatar") {
|
||||||
return "🟣 Avatar Mode (1:1)";
|
return `🟣 Avatar Mode · ${imageSize}`;
|
||||||
}
|
}
|
||||||
if (mode === "art") {
|
if (mode === "art") {
|
||||||
return "🩷 Art Mode (16:9)";
|
const ratio = aspectRatio ?? "16:9";
|
||||||
|
return `🩷 Art Mode (${ratio}) · ${imageSize}`;
|
||||||
}
|
}
|
||||||
return "🔵 Replace Mode";
|
return `🔵 Replace Mode · ${imageSize}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
type OnSendCallback = (
|
type OnSendCallback = (
|
||||||
@@ -130,7 +140,9 @@ type OnSendCallback = (
|
|||||||
)=> void;
|
)=> void;
|
||||||
|
|
||||||
interface InputAreaProperties {
|
interface InputAreaProperties {
|
||||||
|
readonly aspectRatio?: AspectRatio;
|
||||||
readonly hasMessages: boolean;
|
readonly hasMessages: boolean;
|
||||||
|
readonly imageSize: ImageSize;
|
||||||
readonly initialImageBase64?: string;
|
readonly initialImageBase64?: string;
|
||||||
readonly initialImageMime?: string;
|
readonly initialImageMime?: string;
|
||||||
readonly initialImagePreview?: string;
|
readonly initialImagePreview?: string;
|
||||||
@@ -144,7 +156,9 @@ interface InputAreaProperties {
|
|||||||
/**
|
/**
|
||||||
* Renders the input area for composing and sending messages.
|
* Renders the input area for composing and sending messages.
|
||||||
* @param props - The component props.
|
* @param props - The component props.
|
||||||
|
* @param props.aspectRatio - The thread's aspect ratio setting (Art mode only).
|
||||||
* @param props.hasMessages - Whether the thread already has messages (affects replace mode UI).
|
* @param props.hasMessages - Whether the thread already has messages (affects replace mode UI).
|
||||||
|
* @param props.imageSize - The thread's resolution setting.
|
||||||
* @param props.initialImageBase64 - Initial base64 image data to pre-populate.
|
* @param props.initialImageBase64 - Initial base64 image data to pre-populate.
|
||||||
* @param props.initialImageMime - Initial image MIME type to pre-populate.
|
* @param props.initialImageMime - Initial image MIME type to pre-populate.
|
||||||
* @param props.initialImagePreview - Initial image preview URL to pre-populate.
|
* @param props.initialImagePreview - Initial image preview URL to pre-populate.
|
||||||
@@ -156,7 +170,9 @@ interface InputAreaProperties {
|
|||||||
* @returns The JSX element.
|
* @returns The JSX element.
|
||||||
*/
|
*/
|
||||||
const inputArea = ({
|
const inputArea = ({
|
||||||
|
aspectRatio,
|
||||||
hasMessages,
|
hasMessages,
|
||||||
|
imageSize,
|
||||||
initialImageBase64,
|
initialImageBase64,
|
||||||
initialImageMime,
|
initialImageMime,
|
||||||
initialImagePreview,
|
initialImagePreview,
|
||||||
@@ -256,7 +272,8 @@ const inputArea = ({
|
|||||||
if (isInitialReplace && imageBase64 === undefined) {
|
if (isInitialReplace && imageBase64 === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isInitialReplace && text.trim().length === 0) {
|
const hasContent = text.trim().length > 0 || imageBase64 !== undefined;
|
||||||
|
if (!isInitialReplace && !hasContent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,11 +349,14 @@ const inputArea = ({
|
|||||||
: dropZoneInactiveClass,
|
: dropZoneInactiveClass,
|
||||||
].join(" ");
|
].join(" ");
|
||||||
|
|
||||||
|
const hasNoContent = text.trim().length === 0 && imageBase64 === undefined;
|
||||||
|
const isSendDisabled = isLoading || hasNoContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-t border-purple-900/30 p-4 bg-[#0f0a1a]">
|
<div className="border-t border-purple-900/30 p-4 bg-[#0f0a1a]">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<span className="text-xs text-gray-500 uppercase tracking-wider">
|
<span className="text-xs text-gray-500 uppercase tracking-wider">
|
||||||
{modeLabelText(mode)}
|
{modeLabelText(mode, aspectRatio, imageSize)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -397,6 +417,15 @@ const inputArea = ({
|
|||||||
type="file"
|
type="file"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
className={textareaClass}
|
||||||
|
disabled={isLoading}
|
||||||
|
onChange={handleTextChange}
|
||||||
|
placeholder="Add notes to include with the image (optional)..."
|
||||||
|
rows={2}
|
||||||
|
value={text}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={replaceButtonClass}
|
className={replaceButtonClass}
|
||||||
disabled={isLoading || imageBase64 === undefined}
|
disabled={isLoading || imageBase64 === undefined}
|
||||||
@@ -414,41 +443,40 @@ const inputArea = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
: <div className="flex flex-col gap-3">
|
: <div className="flex flex-col gap-3">
|
||||||
{mode === "replace"
|
<div className="flex flex-col gap-2">
|
||||||
? <div className="flex flex-col gap-2">
|
{imagePreview === undefined
|
||||||
{imagePreview === undefined
|
? <button
|
||||||
? <button
|
className={pasteButtonClass}
|
||||||
className={pasteButtonClass}
|
onClick={handlePasteButtonClick}
|
||||||
onClick={handlePasteButtonClick}
|
type="button"
|
||||||
|
>
|
||||||
|
{mode === "replace"
|
||||||
|
? "📋 Paste replacement image (optional)"
|
||||||
|
: "📋 Paste image (optional)"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
: <div className="relative inline-block">
|
||||||
|
<img
|
||||||
|
alt="Upload preview"
|
||||||
|
className="max-h-32 rounded-lg border border-purple-700/40"
|
||||||
|
src={imagePreview}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={clearButtonClass}
|
||||||
|
onClick={clearImage}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{"📋 Paste replacement image (optional)"}
|
{"×"}
|
||||||
</button>
|
</button>
|
||||||
|
</div>}
|
||||||
: <div className="relative inline-block">
|
<input
|
||||||
<img
|
accept="image/*"
|
||||||
alt="Upload preview"
|
className="hidden"
|
||||||
className="max-h-32 rounded-lg border border-purple-700/40"
|
onChange={handleFileChange}
|
||||||
src={imagePreview}
|
ref={fileInputReference}
|
||||||
/>
|
type="file"
|
||||||
<button
|
/>
|
||||||
className={clearButtonClass}
|
</div>
|
||||||
onClick={clearImage}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{"×"}
|
|
||||||
</button>
|
|
||||||
</div>}
|
|
||||||
<input
|
|
||||||
accept="image/*"
|
|
||||||
className="hidden"
|
|
||||||
onChange={handleFileChange}
|
|
||||||
ref={fileInputReference}
|
|
||||||
type="file"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
: null}
|
|
||||||
|
|
||||||
<div className="flex gap-3 items-end">
|
<div className="flex gap-3 items-end">
|
||||||
<textarea
|
<textarea
|
||||||
@@ -464,7 +492,7 @@ const inputArea = ({
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className={sendButtonClass}
|
className={sendButtonClass}
|
||||||
disabled={isLoading || text.trim().length === 0}
|
disabled={isSendDisabled}
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* @file Modal for selecting a new thread generation mode.
|
* @file Modal for selecting a new thread generation mode and options.
|
||||||
* @copyright Naomi Carrigan
|
* @copyright Naomi Carrigan
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
/* eslint-disable max-lines-per-function -- Component JSX inherently requires many lines */
|
/* eslint-disable max-lines-per-function -- Component JSX inherently requires many lines */
|
||||||
import { useCallback, type JSX, type MouseEvent } from "react";
|
/* eslint-disable max-lines -- Two-step modal requires many option definitions */
|
||||||
import type { Mode } from "../types/index.ts";
|
import {
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
type JSX,
|
||||||
|
type MouseEvent,
|
||||||
|
} from "react";
|
||||||
|
import type { AspectRatio, ImageSize, Mode } from "../types/index.ts";
|
||||||
|
|
||||||
interface ModeOption {
|
interface ModeOption {
|
||||||
colour: string;
|
colour: string;
|
||||||
@@ -16,14 +22,25 @@ interface ModeOption {
|
|||||||
mode: Mode;
|
mode: Mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AspectRatioOption {
|
||||||
|
label: string;
|
||||||
|
value: AspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImageSizeOption {
|
||||||
|
description: string;
|
||||||
|
label: string;
|
||||||
|
value: ImageSize;
|
||||||
|
}
|
||||||
|
|
||||||
const avatarDescription = [
|
const avatarDescription = [
|
||||||
"Generate a square 1:1 portrait.",
|
"Generate a square 1:1 portrait.",
|
||||||
"Perfect for profile pictures and avatars.",
|
"Perfect for profile pictures and avatars.",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
|
|
||||||
const artDescription = [
|
const artDescription = [
|
||||||
"Generate wide 16:9 landscape artwork.",
|
"Generate landscape or portrait artwork.",
|
||||||
"Great for wallpapers and banners.",
|
"Choose your aspect ratio and resolution.",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
|
|
||||||
const replaceDescription = [
|
const replaceDescription = [
|
||||||
@@ -55,6 +72,21 @@ const modeOptionList: Array<ModeOption> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const aspectRatioOptions: Array<AspectRatioOption> = [
|
||||||
|
{ label: "1:1 Square", value: "1:1" },
|
||||||
|
{ label: "4:3 Standard", value: "4:3" },
|
||||||
|
{ label: "3:4 Portrait", value: "3:4" },
|
||||||
|
{ label: "16:9 Widescreen", value: "16:9" },
|
||||||
|
{ label: "9:16 Wallpaper", value: "9:16" },
|
||||||
|
{ label: "21:9 Ultrawide", value: "21:9" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const imageSizeOptions: Array<ImageSizeOption> = [
|
||||||
|
{ description: "Faster, cheaper", label: "1K", value: "1K" },
|
||||||
|
{ description: "Balanced", label: "2K", value: "2K" },
|
||||||
|
{ description: "Best quality", label: "4K", value: "4K" },
|
||||||
|
];
|
||||||
|
|
||||||
const colourMap: Record<
|
const colourMap: Record<
|
||||||
string,
|
string,
|
||||||
{ badge: string; button: string; hover: string }
|
{ badge: string; button: string; hover: string }
|
||||||
@@ -82,6 +114,20 @@ const isMode = (value: string): value is Mode => {
|
|||||||
return validModesSet.has(value);
|
return validModesSet.has(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validAspectRatios = new Set<string>([
|
||||||
|
"1:1", "3:4", "4:3", "9:16", "16:9", "21:9",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const isAspectRatio = (value: string): value is AspectRatio => {
|
||||||
|
return validAspectRatios.has(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validImageSizes = new Set<string>([ "1K", "2K", "4K" ]);
|
||||||
|
|
||||||
|
const isImageSize = (value: string): value is ImageSize => {
|
||||||
|
return validImageSizes.has(value);
|
||||||
|
};
|
||||||
|
|
||||||
const overlayClass = [
|
const overlayClass = [
|
||||||
"fixed inset-0 bg-black/70 backdrop-blur-sm",
|
"fixed inset-0 bg-black/70 backdrop-blur-sm",
|
||||||
"flex items-center justify-center z-50",
|
"flex items-center justify-center z-50",
|
||||||
@@ -93,32 +139,243 @@ const panelClass = [
|
|||||||
"shadow-2xl shadow-purple-900/30",
|
"shadow-2xl shadow-purple-900/30",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
|
|
||||||
|
const pillBaseClass = [
|
||||||
|
"px-4 py-2 rounded-xl border text-sm font-medium",
|
||||||
|
"transition-all duration-200 cursor-pointer",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
const pillActiveClass = "border-purple-500 bg-purple-600/30 text-white";
|
||||||
|
|
||||||
|
const pillInactiveClass = [
|
||||||
|
"border-purple-900/40 bg-transparent text-gray-400",
|
||||||
|
"hover:border-purple-500/60 hover:text-gray-200",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
const startButtonClass = [
|
||||||
|
"w-full bg-gradient-to-r from-purple-600 to-pink-600",
|
||||||
|
"hover:from-purple-500 hover:to-pink-500",
|
||||||
|
"text-white font-semibold py-3 rounded-xl",
|
||||||
|
"transition-all duration-200 mt-2",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
const sizeButtonActiveClass = "border-purple-500 bg-purple-600/30 text-white";
|
||||||
|
|
||||||
|
const sizeButtonInactiveClass = [
|
||||||
|
"border-purple-900/40 bg-transparent text-gray-400",
|
||||||
|
"hover:border-purple-500/60 hover:text-gray-200",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
const backButtonClass = [
|
||||||
|
"text-gray-400 hover:text-white",
|
||||||
|
"transition-colors text-xl",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
const sectionLabelClass = [
|
||||||
|
"text-sm text-gray-400",
|
||||||
|
"uppercase tracking-wider mb-3",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
interface NewThreadModalProperties {
|
interface NewThreadModalProperties {
|
||||||
readonly onClose: ()=> void;
|
readonly onClose: ()=> void;
|
||||||
readonly onSelect: (mode: Mode)=> void;
|
readonly onSelect: (
|
||||||
|
mode: Mode,
|
||||||
|
aspectRatio: AspectRatio | undefined,
|
||||||
|
imageSize: ImageSize,
|
||||||
|
)=> void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the new thread modal for selecting a generation mode.
|
* Renders the new thread modal for selecting a generation mode and options.
|
||||||
* @param props - The component props.
|
* @param props - The component props.
|
||||||
* @param props.onClose - Callback to close the modal.
|
* @param props.onClose - Callback to close the modal.
|
||||||
* @param props.onSelect - Callback when a mode is selected.
|
* @param props.onSelect - Callback when a mode and options are confirmed.
|
||||||
* @returns The JSX element.
|
* @returns The JSX element.
|
||||||
*/
|
*/
|
||||||
const threadModal = ({
|
const threadModal = ({
|
||||||
onClose,
|
onClose,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: NewThreadModalProperties): JSX.Element => {
|
}: NewThreadModalProperties): JSX.Element => {
|
||||||
|
const [ step, setStep ] = useState<"mode" | "options">("mode");
|
||||||
|
const [ selectedMode, setSelectedMode ] = useState<Mode | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
const [ aspectRatio, setAspectRatio ] = useState<AspectRatio>("16:9");
|
||||||
|
const [ imageSize, setImageSize ] = useState<ImageSize>("4K");
|
||||||
|
|
||||||
const handleModeSelect = useCallback(
|
const handleModeSelect = useCallback(
|
||||||
(event: MouseEvent<HTMLButtonElement>): void => {
|
(event: MouseEvent<HTMLButtonElement>): void => {
|
||||||
const rawMode = event.currentTarget.dataset.mode;
|
const rawMode = event.currentTarget.dataset.mode;
|
||||||
if (rawMode !== undefined && isMode(rawMode)) {
|
if (rawMode !== undefined && isMode(rawMode)) {
|
||||||
onSelect(rawMode);
|
setSelectedMode(rawMode);
|
||||||
|
setStep("options");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[ onSelect ],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleBack = useCallback((): void => {
|
||||||
|
setStep("mode");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleStart = useCallback((): void => {
|
||||||
|
if (selectedMode === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ratio = selectedMode === "art"
|
||||||
|
? aspectRatio
|
||||||
|
: undefined;
|
||||||
|
onSelect(selectedMode, ratio, imageSize);
|
||||||
|
}, [ selectedMode, aspectRatio, imageSize, onSelect ]);
|
||||||
|
|
||||||
|
const handleAspectRatioSelect = useCallback(
|
||||||
|
(event: MouseEvent<HTMLButtonElement>): void => {
|
||||||
|
const rawRatio = event.currentTarget.dataset.ratio;
|
||||||
|
if (rawRatio !== undefined && isAspectRatio(rawRatio)) {
|
||||||
|
setAspectRatio(rawRatio);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleImageSizeSelect = useCallback(
|
||||||
|
(event: MouseEvent<HTMLButtonElement>): void => {
|
||||||
|
const rawSize = event.currentTarget.dataset.size;
|
||||||
|
if (rawSize !== undefined && isImageSize(rawSize)) {
|
||||||
|
setImageSize(rawSize);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedModeOption = modeOptionList.find((o) => {
|
||||||
|
return o.mode === selectedMode;
|
||||||
|
});
|
||||||
|
const selectedColours = selectedModeOption === undefined
|
||||||
|
? colourMap.purple
|
||||||
|
: colourMap[selectedModeOption.colour];
|
||||||
|
|
||||||
|
if (step === "options" && selectedModeOption !== undefined) {
|
||||||
|
return (
|
||||||
|
<div className={overlayClass}>
|
||||||
|
<div className={panelClass}>
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<button
|
||||||
|
className={backButtonClass}
|
||||||
|
onClick={handleBack}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{"←"}
|
||||||
|
</button>
|
||||||
|
<h2 className="text-2xl font-bold text-white flex-1">
|
||||||
|
{"Configure Thread"}
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
onClick={onClose}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{"×"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
"flex items-center gap-3 mb-6 p-4",
|
||||||
|
"rounded-xl bg-[#241836] border border-purple-900/30",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
<span className="text-3xl">{selectedModeOption.icon}</span>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-white font-semibold">
|
||||||
|
{selectedModeOption.label}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`text-xs px-2 py-0.5 rounded-full ${selectedColours?.badge ?? ""}`}
|
||||||
|
>
|
||||||
|
{selectedModeOption.mode}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 text-xs mt-0.5">
|
||||||
|
{selectedModeOption.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedMode === "art"
|
||||||
|
? <div className="mb-6">
|
||||||
|
<p className={sectionLabelClass}>
|
||||||
|
{"Aspect Ratio"}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{aspectRatioOptions.map((option) => {
|
||||||
|
const pillClass = [
|
||||||
|
pillBaseClass,
|
||||||
|
aspectRatio === option.value
|
||||||
|
? pillActiveClass
|
||||||
|
: pillInactiveClass,
|
||||||
|
].join(" ");
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={pillClass}
|
||||||
|
data-ratio={option.value}
|
||||||
|
key={option.value}
|
||||||
|
onClick={handleAspectRatioSelect}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
: null}
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<p className={sectionLabelClass}>
|
||||||
|
{"Resolution"}
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
{imageSizeOptions.map((option) => {
|
||||||
|
const sizeClass = [
|
||||||
|
"flex-1 flex flex-col items-center py-3 px-2",
|
||||||
|
"rounded-xl border text-sm font-medium",
|
||||||
|
"transition-all duration-200 cursor-pointer",
|
||||||
|
imageSize === option.value
|
||||||
|
? sizeButtonActiveClass
|
||||||
|
: sizeButtonInactiveClass,
|
||||||
|
].join(" ");
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={sizeClass}
|
||||||
|
data-size={option.value}
|
||||||
|
key={option.value}
|
||||||
|
onClick={handleImageSizeSelect}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span className="text-base font-bold">{option.label}</span>
|
||||||
|
<span className="text-xs mt-0.5 opacity-70">
|
||||||
|
{option.description}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={startButtonClass}
|
||||||
|
onClick={handleStart}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{"Start Thread →"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={overlayClass}>
|
<div className={overlayClass}>
|
||||||
<div className={panelClass}>
|
<div className={panelClass}>
|
||||||
|
|||||||
@@ -68,19 +68,19 @@ const modeLabel = (mode: Mode): string => {
|
|||||||
interface BuildUserPartsOptions {
|
interface BuildUserPartsOptions {
|
||||||
readonly imageBase64?: string;
|
readonly imageBase64?: string;
|
||||||
readonly imageMime?: string;
|
readonly imageMime?: string;
|
||||||
readonly mode: Mode;
|
|
||||||
readonly text: string;
|
readonly text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildUserParts = ({
|
const buildUserParts = ({
|
||||||
imageBase64,
|
imageBase64,
|
||||||
imageMime,
|
imageMime,
|
||||||
mode,
|
|
||||||
text,
|
text,
|
||||||
}: BuildUserPartsOptions): Array<MessagePart> => {
|
}: BuildUserPartsOptions): Array<MessagePart> => {
|
||||||
const userParts: Array<MessagePart> = [];
|
const userParts: Array<MessagePart> = [];
|
||||||
|
|
||||||
if (mode === "replace" && imageBase64 !== undefined) {
|
if (imageBase64 === undefined) {
|
||||||
|
userParts.push({ text: text, type: "text" });
|
||||||
|
} else {
|
||||||
userParts.push({
|
userParts.push({
|
||||||
imageData: imageBase64,
|
imageData: imageBase64,
|
||||||
mimeType: imageMime ?? "image/png",
|
mimeType: imageMime ?? "image/png",
|
||||||
@@ -89,8 +89,6 @@ const buildUserParts = ({
|
|||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
userParts.push({ text: text, type: "text" });
|
userParts.push({ text: text, type: "text" });
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
userParts.push({ text: text, type: "text" });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return userParts;
|
return userParts;
|
||||||
@@ -174,13 +172,18 @@ const threadView = ({
|
|||||||
onLoadingChange(true);
|
onLoadingChange(true);
|
||||||
onErrorChange(undefined);
|
onErrorChange(undefined);
|
||||||
|
|
||||||
const { messages, mode } = thread;
|
const {
|
||||||
|
aspectRatio,
|
||||||
|
imageSize: rawImageSize,
|
||||||
|
messages,
|
||||||
|
mode,
|
||||||
|
} = thread;
|
||||||
|
const imageSize = rawImageSize ?? "4K";
|
||||||
const userParts = buildUserParts({
|
const userParts = buildUserParts({
|
||||||
/* eslint-disable-next-line stylistic/no-extra-parens -- Required for conditional spread with exactOptionalPropertyTypes */
|
/* eslint-disable-next-line stylistic/no-extra-parens -- Required for conditional spread with exactOptionalPropertyTypes */
|
||||||
...(imageBase64 !== undefined && { imageBase64 }),
|
...(imageBase64 !== undefined && { imageBase64 }),
|
||||||
/* eslint-disable-next-line stylistic/no-extra-parens -- Required for conditional spread with exactOptionalPropertyTypes */
|
/* eslint-disable-next-line stylistic/no-extra-parens -- Required for conditional spread with exactOptionalPropertyTypes */
|
||||||
...(imageMime !== undefined && { imageMime }),
|
...(imageMime !== undefined && { imageMime }),
|
||||||
mode,
|
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -217,7 +220,9 @@ const threadView = ({
|
|||||||
"send_message",
|
"send_message",
|
||||||
{
|
{
|
||||||
apiKey,
|
apiKey,
|
||||||
|
aspectRatio,
|
||||||
history,
|
history,
|
||||||
|
imageSize,
|
||||||
mode,
|
mode,
|
||||||
userImageBase64,
|
userImageBase64,
|
||||||
userImageMime,
|
userImageMime,
|
||||||
@@ -253,7 +258,13 @@ const threadView = ({
|
|||||||
|
|
||||||
const handleRetry = useCallback(
|
const handleRetry = useCallback(
|
||||||
(modelMessageIndex: number): void => {
|
(modelMessageIndex: number): void => {
|
||||||
const { messages, mode } = thread;
|
const {
|
||||||
|
aspectRatio,
|
||||||
|
imageSize: rawImageSize,
|
||||||
|
messages,
|
||||||
|
mode,
|
||||||
|
} = thread;
|
||||||
|
const imageSize = rawImageSize ?? "4K";
|
||||||
const userMessageIndex = modelMessageIndex - 1;
|
const userMessageIndex = modelMessageIndex - 1;
|
||||||
const userMessage = messages[userMessageIndex];
|
const userMessage = messages[userMessageIndex];
|
||||||
|
|
||||||
@@ -300,7 +311,9 @@ const threadView = ({
|
|||||||
}
|
}
|
||||||
const response = await invoke<SendResult>("send_message", {
|
const response = await invoke<SendResult>("send_message", {
|
||||||
apiKey,
|
apiKey,
|
||||||
|
aspectRatio,
|
||||||
history,
|
history,
|
||||||
|
imageSize,
|
||||||
mode,
|
mode,
|
||||||
userImageBase64,
|
userImageBase64,
|
||||||
userImageMime,
|
userImageMime,
|
||||||
@@ -353,7 +366,13 @@ const threadView = ({
|
|||||||
|
|
||||||
const handleEditCommit = useCallback(
|
const handleEditCommit = useCallback(
|
||||||
(messageIndex: number, editedText: string): void => {
|
(messageIndex: number, editedText: string): void => {
|
||||||
const { messages, mode } = thread;
|
const {
|
||||||
|
aspectRatio,
|
||||||
|
imageSize: rawImageSize,
|
||||||
|
messages,
|
||||||
|
mode,
|
||||||
|
} = thread;
|
||||||
|
const imageSize = rawImageSize ?? "4K";
|
||||||
const originalMessage = messages[messageIndex];
|
const originalMessage = messages[messageIndex];
|
||||||
if (originalMessage === undefined) {
|
if (originalMessage === undefined) {
|
||||||
return;
|
return;
|
||||||
@@ -404,7 +423,9 @@ const threadView = ({
|
|||||||
}
|
}
|
||||||
const response = await invoke<SendResult>("send_message", {
|
const response = await invoke<SendResult>("send_message", {
|
||||||
apiKey,
|
apiKey,
|
||||||
|
aspectRatio,
|
||||||
history,
|
history,
|
||||||
|
imageSize,
|
||||||
mode,
|
mode,
|
||||||
userImageBase64,
|
userImageBase64,
|
||||||
userImageMime,
|
userImageMime,
|
||||||
@@ -536,7 +557,11 @@ const threadView = ({
|
|||||||
{...(pendingInput?.text !== undefined && {
|
{...(pendingInput?.text !== undefined && {
|
||||||
initialText: pendingInput.text,
|
initialText: pendingInput.text,
|
||||||
})}
|
})}
|
||||||
|
{...(thread.aspectRatio !== undefined && {
|
||||||
|
aspectRatio: thread.aspectRatio,
|
||||||
|
})}
|
||||||
hasMessages={thread.messages.length > 0}
|
hasMessages={thread.messages.length > 0}
|
||||||
|
imageSize={thread.imageSize ?? "4K"}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
mode={thread.mode}
|
mode={thread.mode}
|
||||||
onInputChange={onPendingInputChange}
|
onInputChange={onPendingInputChange}
|
||||||
|
|||||||
+20
-7
@@ -5,6 +5,8 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
type AspectRatio = "1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "21:9";
|
||||||
|
type ImageSize = "1K" | "2K" | "4K";
|
||||||
type Mode = "avatar" | "art" | "replace";
|
type Mode = "avatar" | "art" | "replace";
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
@@ -33,12 +35,23 @@ interface ThreadMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Thread {
|
interface Thread {
|
||||||
createdAt: number;
|
aspectRatio?: AspectRatio;
|
||||||
id: string;
|
createdAt: number;
|
||||||
messages: Array<ThreadMessage>;
|
id: string;
|
||||||
mode: Mode;
|
imageSize?: ImageSize;
|
||||||
name: string;
|
messages: Array<ThreadMessage>;
|
||||||
updatedAt: number;
|
mode: Mode;
|
||||||
|
name: string;
|
||||||
|
updatedAt: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { Config, MessagePart, Mode, PendingInput, Thread, ThreadMessage };
|
export type {
|
||||||
|
AspectRatio,
|
||||||
|
Config,
|
||||||
|
ImageSize,
|
||||||
|
MessagePart,
|
||||||
|
Mode,
|
||||||
|
PendingInput,
|
||||||
|
Thread,
|
||||||
|
ThreadMessage,
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user