Merge remote-tracking branch 'origin/main' into feat/instagram

This commit is contained in:
Nevo David 2024-12-04 13:40:31 +07:00
commit 69541c9eec
22 changed files with 6552 additions and 5439 deletions

View File

@ -1,7 +1,7 @@
name: "🙏🏻 Installation Problem"
description: "Report an issue with installation"
title: "Installation Problem"
labels: ["installation"]
labels: ["type: installation"]
body:
- type: markdown
attributes:
@ -13,4 +13,7 @@ body:
attributes:
label: For installation issues, please visit our https://discord.postiz.com for assistance.
description: For installation issues, please visit our [Discord Support](https://discord.postiz.com) for assistance.
placeholder: For installation issues, please visit our https://discord.postiz.com for assistance.
placeholder: |
For installation issues, please visit our https://discord.postiz.com for assistance.
Please do not save this issue - do not submit installation issues on GitHub.

View File

@ -1,8 +1,14 @@
<p align="center">
Please help us out on Product Hunt,<br />to give you the best open-source<br />social media scheduling tool in the world 🌎👇 <br /><br />
<a href="https://www.producthunt.com/posts/postiz?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-postiz" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=606350&theme=light" alt="Postiz - Your&#0032;ultimate&#0032;AI&#0032;social&#0032;media&#0032;scheduling&#0032;tool&#0032; | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<br /><br />
</p>
<p align="center">
<a href="https://postiz.com" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/765e9d72-3ee7-4a56-9d59-a2c9befe2311">
<img alt="Novu Logo" src="https://github.com/user-attachments/assets/f0d30d70-dddb-4142-8876-e9aa6ed1cb99" width="280"/>
<img alt="Postiz Logo" src="https://github.com/user-attachments/assets/f0d30d70-dddb-4142-8876-e9aa6ed1cb99" width="280"/>
</picture>
</a>
</p>
@ -58,22 +64,6 @@
<br />
<p align="center">
<br /><br /><br />
<h1>We participate in Hacktoberfest 2024! 🎉🎊</h1>
<p align="left">We are sending a t-shirt for every merged PR! (max 1 per person)</p>
<p align="left"><strong>Rules:</strong></p>
<ul align="left">
<li>You must create an issue before making a pull request.</li>
<li>You can also ask to be assigned to an issue. During Hacktoberfest, each issue can have multiple assignees.</li>
<li>We have to approve the issue and add a "hacktoberfest" tag.</li>
<li>We encourage everybody to contribute to all types of issues. We will only send swag for issues with features and bug fixes (no typos, sorry).</li>
</ul>
<p align="center"><img align="center" width="400" src="https://github.com/user-attachments/assets/3ceffccc-e4b3-4098-b9ba-44a94cf01294" /></p>
<br /><br /><br />
</p>
<p align="center">
<video src="https://github.com/user-attachments/assets/05436a01-19c8-4827-b57f-05a5e7637a67" width="100%" />
</p>

View File

@ -1,5 +1,12 @@
import {
Body, Controller, Delete, Get, Param, Post, Query, UseFilters
Body,
Controller,
Delete,
Get,
Param,
Post,
Query,
UseFilters,
} from '@nestjs/common';
import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service';
import { ConnectIntegrationDto } from '@gitroom/nestjs-libraries/dtos/integrations/connect.integration.dto';
@ -22,7 +29,11 @@ import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/po
import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto';
import { AuthService } from '@gitroom/helpers/auth/auth.service';
import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
import { NotEnoughScopes } from '@gitroom/nestjs-libraries/integrations/social.abstract';
import {
NotEnoughScopes,
RefreshToken,
} from '@gitroom/nestjs-libraries/integrations/social.abstract';
import { timer } from '@gitroom/helpers/utils/timer';
@ApiTags('Integrations')
@Controller('/integrations')
@ -51,7 +62,7 @@ export class IntegrationsController {
id: p.id,
internalId: p.internalId,
disabled: p.disabled,
picture: p.picture,
picture: p.picture || '/no-picture.jpg',
identifier: p.providerIdentifier,
inBetweenSteps: p.inBetweenSteps,
refreshNeeded: p.refreshNeeded,
@ -201,11 +212,51 @@ export class IntegrationsController {
}
if (integrationProvider[body.name]) {
return integrationProvider[body.name](
getIntegration.token,
body.data,
getIntegration.internalId
);
try {
const load = await integrationProvider[body.name](
getIntegration.token,
body.data,
getIntegration.internalId
);
return load;
} catch (err) {
if (err instanceof RefreshToken) {
const { accessToken, refreshToken, expiresIn } =
await integrationProvider.refreshToken(
getIntegration.refreshToken
);
if (accessToken) {
await this._integrationService.createOrUpdateIntegration(
getIntegration.organizationId,
getIntegration.name,
getIntegration.picture!,
'social',
getIntegration.internalId,
getIntegration.providerIdentifier,
accessToken,
refreshToken,
expiresIn
);
getIntegration.token = accessToken;
if (integrationProvider.refreshWait) {
await timer(10000);
}
return this.functionIntegration(org, body);
} else {
await this._integrationService.disconnectChannel(
org.id,
getIntegration
);
return false;
}
}
return false;
}
}
throw new Error('Function not found');
}
@ -352,7 +403,9 @@ export class IntegrationsController {
}
if (refresh && id !== refresh) {
throw new NotEnoughScopes('Please refresh the channel that needs to be refreshed');
throw new NotEnoughScopes(
'Please refresh the channel that needs to be refreshed'
);
}
return this._integrationService.createOrUpdateIntegration(

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -49,6 +49,15 @@ import { CopilotPopup } from '@copilotkit/react-ui';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import Image from 'next/image';
import { weightedLength } from '@gitroom/helpers/utils/count.length';
function countCharacters(text: string, type: string): number {
if (type !== 'x') {
return text.length;
}
return weightedLength(text);
}
export const AddEditModal: FC<{
date: dayjs.Dayjs;
@ -277,7 +286,9 @@ export const AddEditModal: FC<{
if (
key.value.some(
(p) => p.content.length > (key.maximumCharacters || 1000000)
(p) => {
return countCharacters(p.content, key?.integration?.identifier || '') > (key.maximumCharacters || 1000000);
}
)
) {
if (
@ -385,16 +396,16 @@ export const AddEditModal: FC<{
instructions="You are an assistant that help the user to schedule their social media posts, everytime somebody write something, try to use a function call, if not prompt the user that the request is invalid and you are here to assists with social media posts"
/>
)}
<div
<div
id="add-edit-modal"
className={clsx('flex flex-col md:flex-row p-[10px] rounded-[4px] bg-primary gap-[20px]')}
className={clsx(
'flex flex-col md:flex-row p-[10px] rounded-[4px] bg-primary gap-[20px]'
)}
>
<div
className={clsx(
'flex flex-col gap-[16px] transition-all duration-700 whitespace-nowrap',
!expend.expend
? 'flex-1 animate-overflow'
: 'w-0 overflow-hidden'
!expend.expend ? 'flex-1 animate-overflow' : 'w-0 overflow-hidden'
)}
>
<div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0">
@ -420,7 +431,7 @@ export const AddEditModal: FC<{
) : (
<div
className={clsx(
'relative w-[34px] h-[34px] rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500',
'relative w-[34px] h-[34px] rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500'
)}
>
<Image
@ -542,11 +553,11 @@ export const AddEditModal: FC<{
</div>
<div className="relative min-h-[68px] flex flex-col rounded-[4px] border border-customColor6 bg-sixth">
<div className="gap-[10px] relative flex flex-col justify-center items-center min-h-full pr-[16px]">
<div id = "add-edit-post-dialog-buttons" className="flex flex-row flex-wrap w-full h-full gap-[10px] justify-end items-center">
<Button
className="rounded-[4px]"
onClick={askClose}
>
<div
id="add-edit-post-dialog-buttons"
className="flex flex-row flex-wrap w-full h-full gap-[10px] justify-end items-center"
>
<Button className="rounded-[4px]" onClick={askClose}>
Cancel
</Button>
<Submitted

View File

@ -27,9 +27,18 @@ import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import { groupBy, sortBy } from 'lodash';
import Image from 'next/image';
import { extend } from 'dayjs';
import { isUSCitizen } from './helpers/isuscitizen.utils';
extend(isSameOrAfter);
extend(isSameOrBefore);
const convertTimeFormatBasedOnLocality = (time: number) => {
if (isUSCitizen()) {
return `${time === 12 ? 12 : time%12}:00 ${time >= 12 ? "PM" : "AM"}`
} else {
return `${time}:00`
}
}
export const days = [
'Monday',
'Tuesday',
@ -91,7 +100,7 @@ export const DayView = () => {
.startOf('day')
.add(option[0].time, 'minute')
.local()
.format('HH:mm')}
.format(isUSCitizen() ? "hh:mm A": "HH:mm")}
</div>
<div
key={option[0].time}
@ -140,7 +149,8 @@ export const WeekView = () => {
{hours.map((hour) => (
<Fragment key={hour}>
<div className="p-2 pr-4 bg-secondary text-center items-center justify-center flex">
{hour.toString().padStart(2, '0')}:00
{/* {hour.toString().padStart(2, '0')}:00 */}
{convertTimeFormatBasedOnLocality(hour)}
</div>
{days.map((day, indexDay) => (
<Fragment key={`${day}-${hour}`}>

View File

@ -3,6 +3,7 @@ import { useCalendar } from '@gitroom/frontend/components/launches/calendar.cont
import clsx from 'clsx';
import dayjs from 'dayjs';
import { useCallback } from 'react';
import { isUSCitizen } from './helpers/isuscitizen.utils';
export const Filters = () => {
const week = useCalendar();
@ -12,30 +13,30 @@ export const Filters = () => {
.year(week.currentYear)
.isoWeek(week.currentWeek)
.day(week.currentDay)
.format('DD/MM/YYYY')
.format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY')
: week.display === 'week'
? dayjs()
.year(week.currentYear)
.isoWeek(week.currentWeek)
.startOf('isoWeek')
.format('DD/MM/YYYY') +
.format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY') +
' - ' +
dayjs()
.year(week.currentYear)
.isoWeek(week.currentWeek)
.endOf('isoWeek')
.format('DD/MM/YYYY')
.format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY')
: dayjs()
.year(week.currentYear)
.month(week.currentMonth)
.startOf('month')
.format('DD/MM/YYYY') +
.format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY') +
' - ' +
dayjs()
.year(week.currentYear)
.month(week.currentMonth)
.endOf('month')
.format('DD/MM/YYYY');
.format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY');
const setDay = useCallback(() => {
week.setFilters({

View File

@ -5,6 +5,7 @@ import clsx from 'clsx';
import { VideoOrImage } from '@gitroom/react/helpers/video.or.image';
import { Chakra_Petch } from 'next/font/google';
import { FC } from 'react';
import { textSlicer } from '@gitroom/helpers/utils/count.length';
const chakra = Chakra_Petch({ weight: '400', subsets: ['latin'] });
export const GeneralPreviewComponent: FC<{maximumCharacters?: number}> = (props) => {
@ -14,7 +15,8 @@ export const GeneralPreviewComponent: FC<{maximumCharacters?: number}> = (props)
removeMarkdown: true,
saveBreaklines: true,
specialFunc: (text: string) => {
return text.slice(0, props.maximumCharacters || 10000) + '<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">' + text?.slice(props.maximumCharacters || 10000) + '</mark>';
const {start, end} = textSlicer(integration?.identifier || '', props.maximumCharacters || 10000, text);
return text.slice(start, end) + '<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">' + text?.slice(end) + '</mark>';
},
});

View File

@ -3,6 +3,7 @@ import dayjs from 'dayjs';
import { Calendar, TimeInput } from '@mantine/dates';
import { useClickOutside } from '@mantine/hooks';
import { Button } from '@gitroom/react/form/button';
import { isUSCitizen } from './isuscitizen.utils';
export const DatePicker: FC<{
date: dayjs.Dayjs;
@ -39,7 +40,7 @@ export const DatePicker: FC<{
onClick={changeShow}
ref={ref}
>
<div className="cursor-pointer">{date.format('DD/MM/YYYY HH:mm')}</div>
<div className="cursor-pointer">{date.format(isUSCitizen() ? 'MM/DD/YYYY hh:mm A' : 'DD/MM/YYYY HH:mm')}</div>
<div className="cursor-pointer">
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@ -0,0 +1,5 @@
export const isUSCitizen = () => {
const userLanguage = navigator.language || navigator.languages[0];
return userLanguage.startsWith('en-US')
}

View File

@ -17,7 +17,7 @@ const YoutubeSettings: FC = () => {
const { register, control } = useSettings();
return (
<div className="flex flex-col">
<Input label="Title" {...register('title')} />
<Input label="Title" {...register('title')} maxLength={100} />
<Select label="Type" {...register('type', { value: 'public' })}>
{type.map((t) => (
<option key={t.value} value={t.value}>

View File

@ -210,6 +210,7 @@ export const TeamsComponent = () => {
<Button
className={`!bg-customColor3 !h-[24px] border border-customColor21 rounded-[4px] text-[12px] ${interClass}`}
onClick={remove(p)}
secondary={true}
>
<div className="flex justify-center items-center gap-[4px]">
<div>
@ -222,7 +223,7 @@ export const TeamsComponent = () => {
>
<path
d="M11.8125 3.125H9.625V2.6875C9.625 2.3394 9.48672 2.00556 9.24058 1.75942C8.99444 1.51328 8.6606 1.375 8.3125 1.375H5.6875C5.3394 1.375 5.00556 1.51328 4.75942 1.75942C4.51328 2.00556 4.375 2.3394 4.375 2.6875V3.125H2.1875C2.07147 3.125 1.96019 3.17109 1.87814 3.25314C1.79609 3.33519 1.75 3.44647 1.75 3.5625C1.75 3.67853 1.79609 3.78981 1.87814 3.87186C1.96019 3.95391 2.07147 4 2.1875 4H2.625V11.875C2.625 12.1071 2.71719 12.3296 2.88128 12.4937C3.04538 12.6578 3.26794 12.75 3.5 12.75H10.5C10.7321 12.75 10.9546 12.6578 11.1187 12.4937C11.2828 12.3296 11.375 12.1071 11.375 11.875V4H11.8125C11.9285 4 12.0398 3.95391 12.1219 3.87186C12.2039 3.78981 12.25 3.67853 12.25 3.5625C12.25 3.44647 12.2039 3.33519 12.1219 3.25314C12.0398 3.17109 11.9285 3.125 11.8125 3.125ZM5.25 2.6875C5.25 2.57147 5.29609 2.46019 5.37814 2.37814C5.46019 2.29609 5.57147 2.25 5.6875 2.25H8.3125C8.42853 2.25 8.53981 2.29609 8.62186 2.37814C8.70391 2.46019 8.75 2.57147 8.75 2.6875V3.125H5.25V2.6875ZM10.5 11.875H3.5V4H10.5V11.875ZM6.125 6.1875V9.6875C6.125 9.80353 6.07891 9.91481 5.99686 9.99686C5.91481 10.0789 5.80353 10.125 5.6875 10.125C5.57147 10.125 5.46019 10.0789 5.37814 9.99686C5.29609 9.91481 5.25 9.80353 5.25 9.6875V6.1875C5.25 6.07147 5.29609 5.96019 5.37814 5.87814C5.46019 5.79609 5.57147 5.75 5.6875 5.75C5.80353 5.75 5.91481 5.79609 5.99686 5.87814C6.07891 5.96019 6.125 6.07147 6.125 6.1875ZM8.75 6.1875V9.6875C8.75 9.80353 8.70391 9.91481 8.62186 9.99686C8.53981 10.0789 8.42853 10.125 8.3125 10.125C8.19647 10.125 8.08519 10.0789 8.00314 9.99686C7.92109 9.91481 7.875 9.80353 7.875 9.6875V6.1875C7.875 6.07147 7.92109 5.96019 8.00314 5.87814C8.08519 5.79609 8.19647 5.75 8.3125 5.75C8.42853 5.75 8.53981 5.79609 8.62186 5.87814C8.70391 5.96019 8.75 6.07147 8.75 6.1875Z"
fill="white"
fill="currentColor"
/>
</svg>
</div>

View File

@ -0,0 +1,25 @@
// @ts-ignore
import twitter from 'twitter-text';
export const textSlicer = (
integrationType: string,
end: number,
text: string
): {start: number, end: number} => {
if (integrationType !== 'x') {
return {
start: 0,
end
}
}
const {validRangeEnd, valid} = twitter.parseTweet(text);
return {
start: 0,
end: valid ? end : validRangeEnd
}
};
export const weightedLength = (text: string): number => {
return twitter.parseTweet(text).weightedLength;
}

View File

@ -150,8 +150,8 @@ export class OrganizationRepository {
if (
process.env.STRIPE_PUBLISHABLE_KEY &&
checkForSubscription?.subscription?.subscriptionTier !==
SubscriptionTier.PRO
checkForSubscription?.subscription?.subscriptionTier ===
SubscriptionTier.STANDARD
) {
return false;
}

View File

@ -1,3 +1,5 @@
import { timer } from '@gitroom/helpers/utils/timer';
export class RefreshToken {
constructor(
public identifier: string,
@ -18,7 +20,7 @@ export class NotEnoughScopes {
}
export abstract class SocialAbstract {
async fetch(url: string, options: RequestInit = {}, identifier = '') {
async fetch(url: string, options: RequestInit = {}, identifier = ''): Promise<Response> {
const request = await fetch(url, options);
if (request.status === 200 || request.status === 201) {
@ -33,6 +35,11 @@ export abstract class SocialAbstract {
json = '{}';
}
if (json.includes('rate_limit_exceeded') || json.includes('Rate limit')) {
await timer(2000);
return this.fetch(url, options, identifier);
}
if (request.status === 401 || json.includes('OAuthException')) {
throw new RefreshToken(identifier, json, options.body!);
}

View File

@ -298,8 +298,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider {
accessToken: string,
date: number
): Promise<AnalyticsData[]> {
const until = dayjs().format('YYYY-MM-DD');
const since = dayjs().subtract(date, 'day').format('YYYY-MM-DD');
const until = dayjs().endOf('day').unix()
const since = dayjs().subtract(date, 'day').unix();
const { data } = await (
await this.fetch(

View File

@ -9,7 +9,6 @@ import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import { timer } from '@gitroom/helpers/utils/timer';
import dayjs from 'dayjs';
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
import { string } from 'yup';
export class InstagramProvider
extends SocialAbstract
@ -357,8 +356,8 @@ export class InstagramProvider
accessToken: string,
date: number
): Promise<AnalyticsData[]> {
const until = dayjs().format('YYYY-MM-DD');
const since = dayjs().subtract(date, 'day').format('YYYY-MM-DD');
const until = dayjs().endOf('day').unix();
const since = dayjs().subtract(date, 'day').unix();
const { data, ...all } = await (
await fetch(

View File

@ -323,8 +323,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
accessToken: string,
date: number
): Promise<AnalyticsData[]> {
const until = dayjs().format('YYYY-MM-DD');
const since = dayjs().subtract(date, 'day').format('YYYY-MM-DD');
const until = dayjs().endOf('day').unix();
const since = dayjs().subtract(date, 'day').unix();
const { data, ...all } = await (
await fetch(
@ -332,7 +332,6 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
)
).json();
console.log(data);
return (
data?.map((d: any) => ({
label: capitalize(d.name),

View File

@ -19,8 +19,8 @@ export class XProvider extends SocialAbstract implements SocialProvider {
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
const startingClient = new TwitterApi({
clientId: process.env.TWITTER_CLIENT_ID!,
clientSecret: process.env.TWITTER_CLIENT_SECRET!,
clientId: process.env.TWITTER_CLIENT_ID! || process.env.X_CLIENT_ID!,
clientSecret: process.env.TWITTER_CLIENT_SECRET! || process.env.X_CLIENT_SECRET!,
});
const {
accessToken,

View File

@ -3,6 +3,9 @@ import {
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { Request, Response } from 'express';
import crypto from 'crypto';
import path from 'path';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
const { CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_ACCESS_KEY, CLOUDFLARE_SECRET_ACCESS_KEY, CLOUDFLARE_BUCKETNAME, CLOUDFLARE_BUCKET_URL } =
process.env;
@ -16,12 +19,16 @@ const R2 = new S3Client({
},
});
// Function to generate a random string
function generateRandomString() {
return makeId(20);
}
export default async function handleR2Upload(
endpoint: string,
req: Request,
res: Response
) {
switch (endpoint) {
case 'create-multipart-upload':
return createMultipartUpload(req, res);
@ -39,10 +46,13 @@ export default async function handleR2Upload(
return res.status(404).end();
}
export async function simpleUpload(data: Buffer, key: string, contentType: string) {
export async function simpleUpload(data: Buffer, originalFilename: string, contentType: string) {
const fileExtension = path.extname(originalFilename); // Extract extension
const randomFilename = generateRandomString() + fileExtension; // Append extension
const params = {
Bucket: CLOUDFLARE_BUCKETNAME,
Key: key,
Key: randomFilename,
Body: data,
ContentType: contentType,
};
@ -50,7 +60,7 @@ export async function simpleUpload(data: Buffer, key: string, contentType: strin
const command = new PutObjectCommand({ ...params });
await R2.send(command);
return CLOUDFLARE_BUCKET_URL + '/' + key;
return CLOUDFLARE_BUCKET_URL + '/' + randomFilename;
}
export async function createMultipartUpload(
@ -58,11 +68,13 @@ export async function createMultipartUpload(
res: Response
) {
const { file, fileHash, contentType } = req.body;
const filename = file.name;
const fileExtension = path.extname(file.name); // Extract extension
const randomFilename = generateRandomString() + fileExtension; // Append extension
try {
const params = {
Bucket: CLOUDFLARE_BUCKETNAME,
Key: `resources/${fileHash}/${filename}`,
Key: `${randomFilename}`,
ContentType: contentType,
Metadata: {
'x-amz-meta-file-hash': fileHash,

11723
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -36,10 +36,10 @@
"@aws-sdk/client-s3": "^3.410.0",
"@aws-sdk/s3-request-presigner": "^3.410.0",
"@casl/ability": "^6.5.0",
"@copilotkit/react-core": "1.1.0",
"@copilotkit/react-textarea": "1.1.0",
"@copilotkit/react-ui": "1.1.0",
"@copilotkit/runtime": "1.1.0",
"@copilotkit/react-core": "^1.3.15",
"@copilotkit/react-textarea": "^1.3.15",
"@copilotkit/react-ui": "^1.3.15",
"@copilotkit/runtime": "^1.3.15",
"@hookform/resolvers": "^3.3.4",
"@mantine/core": "^5.10.5",
"@mantine/dates": "^5.10.5",
@ -148,6 +148,7 @@
"tldts": "^6.1.47",
"tslib": "^2.3.0",
"twitter-api-v2": "^1.16.0",
"twitter-text": "^3.1.0",
"use-debounce": "^10.0.0",
"utf-8-validate": "^5.0.10",
"uuid": "^10.0.0",