@ -23,6 +23,7 @@
|
||||
"@google-cloud/storage": "^7.0.1",
|
||||
"@google-cloud/tasks": "^4.0.0",
|
||||
"@graphql-tools/utils": "^9.1.1",
|
||||
"@langchain/openai": "^0.0.14",
|
||||
"@omnivore/content-handler": "1.0.0",
|
||||
"@omnivore/liqe": "1.0.0",
|
||||
"@omnivore/readability": "1.0.0",
|
||||
@ -77,6 +78,7 @@
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwks-rsa": "^2.0.3",
|
||||
"langchain": "^0.1.21",
|
||||
"linkedom": "^0.14.9",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.2.1",
|
||||
|
||||
36
packages/api/src/entity/AISummary.ts
Normal file
36
packages/api/src/entity/AISummary.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from './user'
|
||||
import { LibraryItem } from './library_item'
|
||||
|
||||
@Entity({ name: 'ai_summaries' })
|
||||
export class AISummary {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user!: User
|
||||
|
||||
@ManyToOne(() => LibraryItem)
|
||||
@JoinColumn({ name: 'library_item_id' })
|
||||
libraryItem!: LibraryItem
|
||||
|
||||
@Column('text')
|
||||
summary?: string
|
||||
|
||||
@Column('text')
|
||||
title?: string
|
||||
|
||||
@Column('text')
|
||||
slug?: string
|
||||
|
||||
@CreateDateColumn({ default: () => 'CURRENT_TIMESTAMP' })
|
||||
createdAt!: Date
|
||||
}
|
||||
79
packages/api/src/jobs/ai-summarize.ts
Normal file
79
packages/api/src/jobs/ai-summarize.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { logger } from '../utils/logger'
|
||||
import { loadSummarizationChain } from 'langchain/chains'
|
||||
import { ChatOpenAI } from '@langchain/openai'
|
||||
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'
|
||||
import { authTrx } from '../repository'
|
||||
import { libraryItemRepository } from '../repository/library_item'
|
||||
import { htmlToMarkdown } from '../utils/parser'
|
||||
import { AISummary } from '../entity/AISummary'
|
||||
import { LibraryItemState } from '../entity/library_item'
|
||||
|
||||
export interface AISummarizeJobData {
|
||||
userId: string
|
||||
promptId?: string
|
||||
libraryItemId: string
|
||||
}
|
||||
|
||||
export const AI_SUMMARIZE_JOB_NAME = 'ai-summary-job'
|
||||
|
||||
export const aiSummarize = async (jobData: AISummarizeJobData) => {
|
||||
try {
|
||||
const libraryItem = await authTrx(
|
||||
async (tx) =>
|
||||
tx
|
||||
.withRepository(libraryItemRepository)
|
||||
.findById(jobData.libraryItemId),
|
||||
undefined,
|
||||
jobData.userId
|
||||
)
|
||||
if (!libraryItem || libraryItem.state !== LibraryItemState.Succeeded) {
|
||||
logger.info(
|
||||
`Not ready to summarize library item job state: ${
|
||||
libraryItem?.state ?? 'null'
|
||||
}`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const llm = new ChatOpenAI({
|
||||
configuration: {
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
},
|
||||
})
|
||||
const textSplitter = new RecursiveCharacterTextSplitter({
|
||||
chunkSize: 2000,
|
||||
})
|
||||
|
||||
const document = htmlToMarkdown(libraryItem.readableContent)
|
||||
const docs = await textSplitter.createDocuments([document])
|
||||
const chain = loadSummarizationChain(llm, {
|
||||
type: 'map_reduce', // you can choose from map_reduce, stuff or refine
|
||||
verbose: true, // to view the steps in the console
|
||||
})
|
||||
const response = await chain.call({
|
||||
input_documents: docs,
|
||||
})
|
||||
|
||||
if (typeof response.text !== 'string') {
|
||||
logger.error(`AI summary did not return text`)
|
||||
return
|
||||
}
|
||||
|
||||
const summary = response.text
|
||||
const _ = await authTrx(
|
||||
async (t) => {
|
||||
return t.getRepository(AISummary).save({
|
||||
user: { id: jobData.userId },
|
||||
libraryItem: { id: jobData.libraryItemId },
|
||||
title: libraryItem.title,
|
||||
slug: libraryItem.slug,
|
||||
summary: summary,
|
||||
})
|
||||
},
|
||||
undefined,
|
||||
jobData.userId
|
||||
)
|
||||
} catch (err) {
|
||||
console.log('error creating summary: ', err)
|
||||
}
|
||||
}
|
||||
@ -5,12 +5,18 @@ import { env } from './env'
|
||||
import { ReportType } from './generated/graphql'
|
||||
import { Merge } from './util'
|
||||
import {
|
||||
enqueueAISummarizeJob,
|
||||
enqueueExportItem,
|
||||
enqueueTriggerRuleJob,
|
||||
enqueueWebhookJob,
|
||||
} from './utils/createTask'
|
||||
import { deepDelete } from './utils/helpers'
|
||||
import { buildLogger } from './utils/logger'
|
||||
import {
|
||||
FeatureName,
|
||||
findFeatureByName,
|
||||
getFeatureName,
|
||||
} from './services/features'
|
||||
|
||||
const logger = buildLogger('pubsub')
|
||||
|
||||
@ -82,6 +88,13 @@ export const createPubSubClient = (): PubsubClient => {
|
||||
data,
|
||||
})
|
||||
|
||||
if (await findFeatureByName(FeatureName.AISummaries, userId)) {
|
||||
await enqueueAISummarizeJob({
|
||||
userId,
|
||||
libraryItemId,
|
||||
})
|
||||
}
|
||||
|
||||
return publish(
|
||||
'entityCreated',
|
||||
Buffer.from(JSON.stringify({ type, userId, ...cleanData }))
|
||||
|
||||
@ -43,6 +43,7 @@ import { redisDataSource } from './redis_data_source'
|
||||
import { CACHED_READING_POSITION_PREFIX } from './services/cached_reading_position'
|
||||
import { getJobPriority } from './utils/createTask'
|
||||
import { logger } from './utils/logger'
|
||||
import { AI_SUMMARIZE_JOB_NAME, aiSummarize } from './jobs/ai-summarize'
|
||||
|
||||
export const QUEUE_NAME = 'omnivore-backend-queue'
|
||||
export const JOB_VERSION = 'v001'
|
||||
@ -113,6 +114,8 @@ export const createWorker = (connection: ConnectionOptions) =>
|
||||
return callWebhook(job.data)
|
||||
case EXPORT_ITEM_JOB_NAME:
|
||||
return exportItem(job.data)
|
||||
case AI_SUMMARIZE_JOB_NAME:
|
||||
return aiSummarize(job.data)
|
||||
case EXPORT_ALL_ITEMS_JOB_NAME:
|
||||
return exportAllItems(job.data)
|
||||
}
|
||||
|
||||
@ -141,6 +141,8 @@ import { markEmailAsItemResolver, recentEmailsResolver } from './recent_emails'
|
||||
import { recentSearchesResolver } from './recent_searches'
|
||||
import { WithDataSourcesContext } from './types'
|
||||
import { updateEmailResolver } from './user'
|
||||
import { getAISummary } from '../services/ai-summaries'
|
||||
import { findUserFeatures, getFeatureName } from '../services/features'
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
type ResultResolveType = {
|
||||
@ -346,6 +348,16 @@ export const functionResolvers = {
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
async features(
|
||||
user: User,
|
||||
__: Record<string, unknown>,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (!ctx.claims?.uid) {
|
||||
return undefined
|
||||
}
|
||||
return findUserFeatures(ctx.claims.uid)
|
||||
},
|
||||
},
|
||||
Article: {
|
||||
async url(article: Article, _: unknown, ctx: WithDataSourcesContext) {
|
||||
@ -485,6 +497,15 @@ export const functionResolvers = {
|
||||
|
||||
return []
|
||||
},
|
||||
async aiSummary(item: SearchItem, _: unknown, ctx: WithDataSourcesContext) {
|
||||
return (
|
||||
await getAISummary({
|
||||
userId: ctx.uid,
|
||||
libraryItemId: item.id,
|
||||
idx: 'latest',
|
||||
})
|
||||
)?.summary
|
||||
},
|
||||
async highlights(
|
||||
item: {
|
||||
id: string
|
||||
|
||||
55
packages/api/src/routers/ai_summary_router.ts
Normal file
55
packages/api/src/routers/ai_summary_router.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { htmlToSpeechFile } from '@omnivore/text-to-speech-handler'
|
||||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import { userRepository } from '../repository/user'
|
||||
import { getClaimsByToken } from '../utils/auth'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
import { getAISummary } from '../services/ai-summaries'
|
||||
|
||||
export function aiSummariesRouter() {
|
||||
const router = express.Router()
|
||||
|
||||
// Get an indexed summary for an individual library item
|
||||
router.get(
|
||||
'/library-item/:libraryItemId/:idx',
|
||||
cors<express.Request>(corsConfig),
|
||||
async (req, res) => {
|
||||
const token = req?.cookies?.auth || req?.headers?.authorization
|
||||
const claims = await getClaimsByToken(token)
|
||||
if (!claims) {
|
||||
return res.status(401).send('UNAUTHORIZED')
|
||||
}
|
||||
|
||||
const { uid } = claims
|
||||
const user = await userRepository.findById(uid)
|
||||
if (!user) {
|
||||
return res.status(400).send('Bad Request')
|
||||
}
|
||||
|
||||
const libraryItemId = req.params.libraryItemId
|
||||
console.log('params: ', req.params)
|
||||
if (!libraryItemId) {
|
||||
return res.status(400).send('Bad request - no library item id provided')
|
||||
}
|
||||
|
||||
const idx = req.params.idx
|
||||
if (!idx) {
|
||||
return res.status(400).send('Bad request - no idx provided')
|
||||
}
|
||||
|
||||
const result = await getAISummary({
|
||||
userId: user.id,
|
||||
idx: req.params.idx,
|
||||
libraryItemId: req.params.libraryItemId,
|
||||
})
|
||||
|
||||
return res.send({
|
||||
summary: result?.summary,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return router
|
||||
}
|
||||
@ -88,6 +88,7 @@ const schema = gql`
|
||||
email: String
|
||||
source: String
|
||||
intercomHash: String
|
||||
features: [String]
|
||||
}
|
||||
|
||||
type Profile {
|
||||
@ -1647,6 +1648,7 @@ const schema = gql`
|
||||
previewContentType: String
|
||||
links: JSON
|
||||
folder: String!
|
||||
aiSummary: String
|
||||
}
|
||||
|
||||
type SearchItemEdge {
|
||||
|
||||
@ -48,6 +48,7 @@ import {
|
||||
} from './utils/auth'
|
||||
import { corsConfig } from './utils/corsConfig'
|
||||
import { buildLogger, buildLoggerTransport } from './utils/logger'
|
||||
import { aiSummariesRouter } from './routers/ai_summary_router'
|
||||
|
||||
const PORT = process.env.PORT || 4000
|
||||
|
||||
@ -121,6 +122,7 @@ export const createApp = (): {
|
||||
app.use('/api/page', pageRouter())
|
||||
app.use('/api/user', userRouter())
|
||||
app.use('/api/article', articleRouter())
|
||||
app.use('/api/ai-summary', aiSummariesRouter())
|
||||
app.use('/api/text-to-speech', textToSpeechRouter())
|
||||
app.use('/api/notification', notificationRouter())
|
||||
app.use('/api/integration', integrationRouter())
|
||||
|
||||
34
packages/api/src/services/ai-summaries.ts
Normal file
34
packages/api/src/services/ai-summaries.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { AISummary } from '../entity/AISummary'
|
||||
import { authTrx } from '../repository'
|
||||
|
||||
export const getAISummary = async (data: {
|
||||
userId: string
|
||||
idx: string
|
||||
libraryItemId: string
|
||||
}): Promise<AISummary | undefined> => {
|
||||
const aiSummary = await authTrx(
|
||||
async (t) => {
|
||||
const repo = t.getRepository(AISummary)
|
||||
if (data.idx == 'latest') {
|
||||
return repo.findOne({
|
||||
where: {
|
||||
user: { id: data.userId },
|
||||
libraryItem: { id: data.libraryItemId },
|
||||
},
|
||||
order: { createdAt: 'DESC' },
|
||||
})
|
||||
} else {
|
||||
return repo.findOne({
|
||||
where: {
|
||||
id: data.idx,
|
||||
user: { id: data.userId },
|
||||
libraryItem: { id: data.libraryItemId },
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
data.userId
|
||||
)
|
||||
return aiSummary ?? undefined
|
||||
}
|
||||
@ -7,6 +7,7 @@ import { getRepository } from '../repository'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
export enum FeatureName {
|
||||
AISummaries = 'ai-summaries',
|
||||
UltraRealisticVoice = 'ultra-realistic-voice',
|
||||
}
|
||||
|
||||
@ -21,7 +22,6 @@ export const optInFeature = async (
|
||||
if (name === FeatureName.UltraRealisticVoice) {
|
||||
return optInUltraRealisticVoice(uid)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
@ -100,6 +100,16 @@ export const signFeatureToken = (
|
||||
)
|
||||
}
|
||||
|
||||
export const findUserFeatures = async (userId: string): Promise<string[]> => {
|
||||
return (
|
||||
await getRepository(Feature).find({
|
||||
where: {
|
||||
user: { id: userId },
|
||||
},
|
||||
})
|
||||
).map((feature) => feature.name)
|
||||
}
|
||||
|
||||
export const findFeatureByName = async (
|
||||
name: FeatureName,
|
||||
userId: string
|
||||
|
||||
@ -44,6 +44,7 @@ import { CreateTaskError } from './errors'
|
||||
import { stringToHash } from './helpers'
|
||||
import { logger } from './logger'
|
||||
import View = google.cloud.tasks.v2.Task.View
|
||||
import { AISummarizeJobData, AI_SUMMARIZE_JOB_NAME } from '../jobs/ai-summarize'
|
||||
|
||||
// Instantiates a client.
|
||||
const client = new CloudTasksClient()
|
||||
@ -66,6 +67,7 @@ export const getJobPriority = (jobName: string): number => {
|
||||
case TRIGGER_RULE_JOB_NAME:
|
||||
case CALL_WEBHOOK_JOB_NAME:
|
||||
case EXPORT_ITEM_JOB_NAME:
|
||||
case AI_SUMMARIZE_JOB_NAME:
|
||||
return 5
|
||||
case BULK_ACTION_JOB_NAME:
|
||||
case `${REFRESH_FEED_JOB_NAME}_high`:
|
||||
@ -694,6 +696,18 @@ export const enqueueWebhookJob = async (data: CallWebhookJobData) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const enqueueAISummarizeJob = async (data: AISummarizeJobData) => {
|
||||
const queue = await getBackendQueue()
|
||||
if (!queue) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return queue.add(AI_SUMMARIZE_JOB_NAME, data, {
|
||||
priority: getJobPriority(AI_SUMMARIZE_JOB_NAME),
|
||||
attempts: 3,
|
||||
})
|
||||
}
|
||||
|
||||
export const bulkEnqueueUpdateLabels = async (data: UpdateLabelsData[]) => {
|
||||
const queue = await getBackendQueue()
|
||||
if (!queue) {
|
||||
|
||||
31
packages/db/migrations/0166.do.add_ai_summary_tables.sql
Executable file
31
packages/db/migrations/0166.do.add_ai_summary_tables.sql
Executable file
@ -0,0 +1,31 @@
|
||||
-- Type: DO
|
||||
-- Name: add_ai_summary_tables
|
||||
-- Description: Add tables for generating AI summaries
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE omnivore.ai_prompts (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
user_id uuid NOT NULL REFERENCES omnivore.user ON DELETE CASCADE,
|
||||
prompt TEXT NOT NULL,
|
||||
created_at timestamptz NOT NULL default current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE omnivore.ai_summaries (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
user_id uuid NOT NULL REFERENCES omnivore.user ON DELETE CASCADE,
|
||||
-- prompt_id uuid NOT NULL REFERENCES omnivore.ai_prompts ON DELETE CASCADE,
|
||||
library_item_id uuid NOT NULL REFERENCES omnivore.library_item ON DELETE CASCADE,
|
||||
summary TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
created_at timestamptz NOT NULL default current_timestamp
|
||||
);
|
||||
|
||||
CREATE POLICY create_summary on omnivore.ai_summaries
|
||||
FOR INSERT TO omnivore_user
|
||||
WITH CHECK (true);
|
||||
|
||||
GRANT SELECT, INSERT ON omnivore.ai_summaries TO omnivore_user;
|
||||
|
||||
COMMIT;
|
||||
12
packages/db/migrations/0166.undo.add_ai_summary_tables.sql
Executable file
12
packages/db/migrations/0166.undo.add_ai_summary_tables.sql
Executable file
@ -0,0 +1,12 @@
|
||||
-- Type: UNDO
|
||||
-- Name: add_ai_summary_tables
|
||||
-- Description: Add tables for generating AI summaries
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE omnivore.ai_summaries;
|
||||
|
||||
DROP TABLE omnivore.ai_prompts;
|
||||
|
||||
|
||||
COMMIT;
|
||||
@ -33,6 +33,26 @@ export const Button = styled('button', {
|
||||
border: '0px solid $ctaBlue',
|
||||
},
|
||||
},
|
||||
tldr: {
|
||||
gap: '10px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
borderRadius: '100px',
|
||||
px: '10px',
|
||||
py: '10px',
|
||||
|
||||
fontFamily: '$inter',
|
||||
fontSize: '12px',
|
||||
fontWeight: '500',
|
||||
cursor: 'pointer',
|
||||
color: '#EDEDED',
|
||||
border: '1px solid $thLibraryMultiselectHover',
|
||||
bg: 'transparent',
|
||||
'&:hover': {
|
||||
opacity: '0.6',
|
||||
border: '1px solid $ctaBlue',
|
||||
},
|
||||
},
|
||||
|
||||
ctaDarkYellow: {
|
||||
border: '1px solid transparent',
|
||||
|
||||
@ -36,6 +36,7 @@ export const FormInput = styled('input', {
|
||||
textIndent: '8px',
|
||||
marginBottom: '2px',
|
||||
height: '38px',
|
||||
pl: '10px',
|
||||
color: '$grayTextContrast',
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
|
||||
53
packages/web/components/elements/icons/AIPromotIcon.tsx
Normal file
53
packages/web/components/elements/icons/AIPromotIcon.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class AIPromptIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
return (
|
||||
<svg
|
||||
width="31"
|
||||
height="30"
|
||||
viewBox="0 0 31 30"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.757812"
|
||||
width="30"
|
||||
height="30"
|
||||
rx="15"
|
||||
fill="#D0A3FF"
|
||||
fillOpacity="0.1"
|
||||
/>
|
||||
<rect
|
||||
x="1.25781"
|
||||
y="0.5"
|
||||
width="29"
|
||||
height="29"
|
||||
rx="14.5"
|
||||
stroke="#D0A3FF"
|
||||
strokeOpacity="0.3"
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M11.0911 11.667L14.4244 15.0003L11.0911 18.3337"
|
||||
stroke="#D0A3FF"
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.7578 19.667H20.4245"
|
||||
stroke="#D0A3FF"
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
61
packages/web/components/elements/icons/BrowserIcon.tsx
Normal file
61
packages/web/components/elements/icons/BrowserIcon.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class BrowserIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M2.5 10C2.5 10.9849 2.69399 11.9602 3.0709 12.8701C3.44781 13.7801 4.00026 14.6069 4.6967 15.3033C5.39314 15.9997 6.21993 16.5522 7.12987 16.9291C8.03982 17.306 9.01509 17.5 10 17.5C10.9849 17.5 11.9602 17.306 12.8701 16.9291C13.7801 16.5522 14.6069 15.9997 15.3033 15.3033C15.9997 14.6069 16.5522 13.7801 16.9291 12.8701C17.306 11.9602 17.5 10.9849 17.5 10C17.5 8.01088 16.7098 6.10322 15.3033 4.6967C13.8968 3.29018 11.9891 2.5 10 2.5C8.01088 2.5 6.10322 3.29018 4.6967 4.6967C3.29018 6.10322 2.5 8.01088 2.5 10Z"
|
||||
stroke={color}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 7.5H17"
|
||||
stroke={color}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 12.5H17"
|
||||
stroke={color}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M9.58322 2.5C8.17934 4.74968 7.43506 7.34822 7.43506 10C7.43506 12.6518 8.17934 15.2503 9.58322 17.5"
|
||||
stroke={color}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.4167 2.5C11.8206 4.74968 12.5649 7.34822 12.5649 10C12.5649 12.6518 11.8206 15.2503 10.4167 17.5"
|
||||
stroke={color}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<defs></defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
import { SpanBox } from '../LayoutPrimitives'
|
||||
|
||||
import React from 'react'
|
||||
import { MultiSelectMode } from '../../templates/homeFeed/LibraryHeader'
|
||||
@ -16,147 +15,112 @@ export const HeaderCheckboxIcon = (
|
||||
switch (props.multiSelectMode) {
|
||||
case 'search':
|
||||
case 'visible':
|
||||
return <HeaderCheckboxCheckedIcon />
|
||||
return <HeaderCheckboxCheckedIcon size={17} />
|
||||
case 'none':
|
||||
case 'off':
|
||||
return <HeaderCheckboxUncheckedIcon />
|
||||
return <HeaderCheckboxUncheckedIcon size={17} />
|
||||
case 'some':
|
||||
return <HeaderCheckboxHalfCheckedIcon />
|
||||
return <HeaderCheckboxHalfCheckedIcon size={17} />
|
||||
}
|
||||
}
|
||||
|
||||
export class HeaderCheckboxUncheckedIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
'--inner-color': 'var(--colors-thHeaderIconInner)',
|
||||
'--ring-color': 'var(--colors-thHeaderIconRing)',
|
||||
'&:hover': {
|
||||
'--inner-color': 'white',
|
||||
'--ring-fill': '#007AFF',
|
||||
'--ring-color': '#007AFF',
|
||||
},
|
||||
}}
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
<g>
|
||||
<path
|
||||
d="M3.479 4.16667C3.479 3.72464 3.6546 3.30072 3.96716 2.98816C4.27972 2.67559 4.70364 2.5 5.14567 2.5H16.8123C17.2544 2.5 17.6783 2.67559 17.9908 2.98816C18.3034 3.30072 18.479 3.72464 18.479 4.16667V15.8333C18.479 16.2754 18.3034 16.6993 17.9908 17.0118C17.6783 17.3244 17.2544 17.5 16.8123 17.5H5.14567C4.70364 17.5 4.27972 17.3244 3.96716 17.0118C3.6546 16.6993 3.479 16.2754 3.479 15.8333V4.16667Z"
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
stroke: 'var(--checkbox-color)',
|
||||
}}
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M12.5 14.1667C12.5 13.7246 12.6756 13.3007 12.9882 12.9882C13.3007 12.6756 13.7246 12.5 14.1667 12.5H25.8333C26.2754 12.5 26.6993 12.6756 27.0118 12.9882C27.3244 13.3007 27.5 13.7246 27.5 14.1667V25.8333C27.5 26.2754 27.3244 26.6993 27.0118 27.0118C26.6993 27.3244 26.2754 27.5 25.8333 27.5H14.1667C13.7246 27.5 13.3007 27.3244 12.9882 27.0118C12.6756 26.6993 12.5 26.2754 12.5 25.8333V14.1667Z"
|
||||
style={{
|
||||
stroke: 'var(--inner-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</SpanBox>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class HeaderCheckboxCheckedIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
'--inner-color': 'var(--colors-thHeaderIconInner)',
|
||||
'--ring-color': 'var(--colors-thHeaderIconRing)',
|
||||
'&:hover': {
|
||||
'--ring-fill': '#007AFF10',
|
||||
},
|
||||
}}
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
width="41"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
<g>
|
||||
<path
|
||||
d="M3.479 4.16667C3.479 3.72464 3.6546 3.30072 3.96716 2.98816C4.27972 2.67559 4.70364 2.5 5.14567 2.5H16.8123C17.2544 2.5 17.6783 2.67559 17.9908 2.98816C18.3034 3.30072 18.479 3.72464 18.479 4.16667V15.8333C18.479 16.2754 18.3034 16.6993 17.9908 17.0118C17.6783 17.3244 17.2544 17.5 16.8123 17.5H5.14567C4.70364 17.5 4.27972 17.3244 3.96716 17.0118C3.6546 16.6993 3.479 16.2754 3.479 15.8333V4.16667Z"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
stroke: 'var(--checkbox-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M25.9341 11.6667C27.5674 11.6667 28.9007 12.9476 28.9857 14.5601L28.9899 14.7226V25.2776C28.9899 26.9109 27.7091 28.2442 26.0966 28.3292L25.9341 28.3334H15.3791C14.5967 28.3335 13.8442 28.0334 13.2764 27.4951C12.7087 26.9569 12.369 26.2213 12.3274 25.4401L12.3232 25.2776V14.7226C12.3232 13.0892 13.6041 11.7559 15.2166 11.6709L15.3791 11.6667H25.9341ZM23.7457 17.7442C23.5895 17.588 23.3775 17.5003 23.1566 17.5003C22.9356 17.5003 22.7237 17.588 22.5674 17.7442L19.8232 20.4876L18.7457 19.4109L18.6674 19.3417C18.4999 19.2122 18.2894 19.1513 18.0786 19.1714C17.8679 19.1915 17.6726 19.291 17.5326 19.4498C17.3926 19.6087 17.3183 19.8148 17.3247 20.0264C17.3312 20.2381 17.418 20.4393 17.5674 20.5892L19.2341 22.2559L19.3124 22.3251C19.4727 22.4495 19.673 22.5111 19.8755 22.4983C20.078 22.4856 20.2689 22.3994 20.4124 22.2559L23.7457 18.9226L23.8149 18.8442C23.9393 18.6839 24.0009 18.4837 23.9881 18.2812C23.9754 18.0787 23.8892 17.8877 23.7457 17.7442Z"
|
||||
fill="#007AFF"
|
||||
d="M7.73877 10.0004L10.0536 12.3152L14.6832 7.68555"
|
||||
style={{
|
||||
stroke: 'var(--checkbox-color)',
|
||||
}}
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</SpanBox>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class HeaderCheckboxHalfCheckedIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
'--inner-color': '#007AFF',
|
||||
'--ring-color': 'var(--colors-thHeaderIconRing)',
|
||||
'&:hover': {
|
||||
'--ring-fill': '#007AFF10',
|
||||
},
|
||||
}}
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
width="41"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
<g>
|
||||
<path
|
||||
d="M3.479 4.16667C3.479 3.72464 3.6546 3.30072 3.96716 2.98816C4.27972 2.67559 4.70364 2.5 5.14567 2.5H16.8123C17.2544 2.5 17.6783 2.67559 17.9908 2.98816C18.3034 3.30072 18.479 3.72464 18.479 4.16667V15.8333C18.479 16.2754 18.3034 16.6993 17.9908 17.0118C17.6783 17.3244 17.2544 17.5 16.8123 17.5H5.14567C4.70364 17.5 4.27972 17.3244 3.96716 17.0118C3.6546 16.6993 3.479 16.2754 3.479 15.8333V4.16667Z"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
stroke: 'var(--checkbox-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M25.8332 11.7496C26.4962 11.7496 27.1321 12.013 27.6009 12.4819C28.0698 12.9507 28.3332 13.5866 28.3332 14.2496V25.9163C28.3332 26.5793 28.0698 27.2152 27.6009 27.6841C27.1321 28.1529 26.4962 28.4163 25.8332 28.4163H14.1665C13.5035 28.4163 12.8676 28.1529 12.3987 27.6841C11.9299 27.2152 11.6665 26.5793 11.6665 25.9163V14.2496C11.6665 13.5866 11.9299 12.9507 12.3987 12.4819C12.8676 12.013 13.5035 11.7496 14.1665 11.7496H25.8332ZM22.4998 19.2496H17.4998L17.4023 19.2555C17.1914 19.2806 16.998 19.3852 16.8617 19.5481C16.7254 19.711 16.6564 19.9198 16.6689 20.1318C16.6813 20.3438 16.7743 20.5431 16.9287 20.6889C17.0831 20.8347 17.2874 20.9161 17.4998 20.9163H22.4998L22.5973 20.9105C22.8082 20.8854 23.0016 20.7807 23.1379 20.6178C23.2743 20.4549 23.3433 20.2462 23.3308 20.0341C23.3184 19.8221 23.2254 19.6228 23.071 19.477C22.9165 19.3312 22.7122 19.2499 22.4998 19.2496Z"
|
||||
fill="#007AFF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</SpanBox>
|
||||
<path
|
||||
d="M6.979 10L14.979 10"
|
||||
style={{
|
||||
stroke: 'var(--checkbox-color)',
|
||||
}}
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
111
packages/web/components/elements/icons/HeaderToggleTLDRIcon.tsx
Normal file
111
packages/web/components/elements/icons/HeaderToggleTLDRIcon.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { SpanBox } from '../LayoutPrimitives'
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class HeaderToggleTLDRIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
'--inner-color': 'var(--colors-thHeaderIconInner)',
|
||||
'--ring-color': 'var(--colors-thHeaderIconRing)',
|
||||
'&:hover': {
|
||||
'--inner-color': 'white',
|
||||
'--ring-fill': '#007AFF',
|
||||
'--ring-color': '#007AFF',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
}}
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M24.2299 16.9786H20.8659L24.4917 11.3988C24.5522 11.2981 24.5522 11.1974 24.5119 11.0967C24.4515 10.996 24.3306 10.9355 24.2299 10.9355H17.8847C17.7437 10.9355 17.6229 11.0363 17.5826 11.1773L15.4675 20.2418C15.4474 20.3224 15.4675 20.4231 15.5279 20.5036C15.5884 20.5842 15.6689 20.6245 15.7697 20.6245H18.3883L16.092 28.6818C16.0517 28.8229 16.1121 28.984 16.2531 29.0444C16.2934 29.0646 16.3337 29.0646 16.374 29.0646C16.4747 29.0646 16.5754 29.0243 16.6157 28.9236L24.4716 17.4419C24.532 17.3411 24.532 17.2203 24.4917 17.1397C24.4515 17.039 24.3507 16.9786 24.2299 16.9786Z"
|
||||
style={{
|
||||
stroke: 'var(--inner-color)',
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
{/*
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
}}
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M13.3333 14.1654C13.3333 13.9444 13.4211 13.7324 13.5774 13.5761C13.7337 13.4198 13.9457 13.332 14.1667 13.332H17.5C17.721 13.332 17.933 13.4198 18.0893 13.5761C18.2455 13.7324 18.3333 13.9444 18.3333 14.1654V17.4987C18.3333 17.7197 18.2455 17.9317 18.0893 18.088C17.933 18.2442 17.721 18.332 17.5 18.332H14.1667C13.9457 18.332 13.7337 18.2442 13.5774 18.088C13.4211 17.9317 13.3333 17.7197 13.3333 17.4987V14.1654Z"
|
||||
style={{
|
||||
stroke: 'var(--inner-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M21.6667 14.1654C21.6667 13.9444 21.7545 13.7324 21.9107 13.5761C22.067 13.4198 22.279 13.332 22.5 13.332H25.8333C26.0543 13.332 26.2663 13.4198 26.4226 13.5761C26.5789 13.7324 26.6667 13.9444 26.6667 14.1654V17.4987C26.6667 17.7197 26.5789 17.9317 26.4226 18.088C26.2663 18.2442 26.0543 18.332 25.8333 18.332H22.5C22.279 18.332 22.067 18.2442 21.9107 18.088C21.7545 17.9317 21.6667 17.7197 21.6667 17.4987V14.1654Z"
|
||||
style={{
|
||||
stroke: 'var(--inner-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M13.3333 22.5013C13.3333 22.2803 13.4211 22.0683 13.5774 21.912C13.7337 21.7558 13.9457 21.668 14.1667 21.668H17.5C17.721 21.668 17.933 21.7558 18.0893 21.912C18.2455 22.0683 18.3333 22.2803 18.3333 22.5013V25.8346C18.3333 26.0556 18.2455 26.2676 18.0893 26.4239C17.933 26.5802 17.721 26.668 17.5 26.668H14.1667C13.9457 26.668 13.7337 26.5802 13.5774 26.4239C13.4211 26.2676 13.3333 26.0556 13.3333 25.8346V22.5013Z"
|
||||
style={{
|
||||
stroke: 'var(--inner-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M21.6667 22.5013C21.6667 22.2803 21.7545 22.0683 21.9107 21.912C22.067 21.7558 22.279 21.668 22.5 21.668H25.8333C26.0543 21.668 26.2663 21.7558 26.4226 21.912C26.5789 22.0683 26.6667 22.2803 26.6667 22.5013V25.8346C26.6667 26.0556 26.5789 26.2676 26.4226 26.4239C26.2663 26.5802 26.0543 26.668 25.8333 26.668H22.5C22.279 26.668 22.067 26.5802 21.9107 26.4239C21.7545 26.2676 21.6667 26.0556 21.6667 25.8346V22.5013Z"
|
||||
style={{
|
||||
stroke: 'var(--inner-color)',
|
||||
}}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg> */}
|
||||
</SpanBox>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -28,7 +28,7 @@ export const HighlightHoverActions = (props: HighlightHoverActionsProps) => {
|
||||
<Box
|
||||
css={{
|
||||
height: '33px',
|
||||
width: '135px',
|
||||
minWidth: '150px',
|
||||
bg: '$thBackground',
|
||||
display: 'flex',
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
|
||||
overflow: 'hidden',
|
||||
cursor: 'pointer',
|
||||
'@media (max-width: 930px)': {
|
||||
m: '15px',
|
||||
// m: '15px',
|
||||
width: 'calc(100% - 30px)',
|
||||
},
|
||||
}}
|
||||
@ -101,7 +101,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{!isTouchScreenDevice() && (
|
||||
{!isTouchScreenDevice() && props.multiSelectMode == 'off' && (
|
||||
<Box
|
||||
ref={refs.setFloating}
|
||||
style={{ ...floatingStyles, zIndex: 3 }}
|
||||
@ -211,7 +211,7 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
m: '10px',
|
||||
m: '12px',
|
||||
lineHeight: '1',
|
||||
}}
|
||||
>
|
||||
|
||||
@ -12,6 +12,7 @@ import { NotebookIcon } from '../../elements/icons/NotebookIcon'
|
||||
import { TrashIcon } from '../../elements/icons/TrashIcon'
|
||||
import { LabelIcon } from '../../elements/icons/LabelIcon'
|
||||
import { UnarchiveIcon } from '../../elements/icons/UnarchiveIcon'
|
||||
import { BrowserIcon } from '../../elements/icons/BrowserIcon'
|
||||
|
||||
type LibraryHoverActionsProps = {
|
||||
viewer: UserBasicData
|
||||
@ -31,7 +32,7 @@ export const LibraryHoverActions = (props: LibraryHoverActionsProps) => {
|
||||
overflow: 'clip',
|
||||
|
||||
height: '33px',
|
||||
width: '184px',
|
||||
width: '200px',
|
||||
bg: '$thBackground',
|
||||
display: 'flex',
|
||||
|
||||
@ -120,7 +121,10 @@ export const LibraryHoverActions = (props: LibraryHoverActionsProps) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<Share size={21} color={theme.colors.thNotebookSubtle.toString()} />
|
||||
<BrowserIcon
|
||||
size={21}
|
||||
color={theme.colors.thNotebookSubtle.toString()}
|
||||
/>
|
||||
</Button>
|
||||
<CardMenu
|
||||
item={props.item}
|
||||
|
||||
@ -102,7 +102,7 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{!isTouchScreenDevice() && (
|
||||
{!isTouchScreenDevice() && props.multiSelectMode == 'off' && (
|
||||
<Box
|
||||
ref={refs.setFloating}
|
||||
style={{ ...floatingStyles, zIndex: 3 }}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { DetailedHTMLProps, MetaHTMLAttributes } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { useDarkModeListener } from '../../lib/hooks/useDarkModeListener'
|
||||
import { webBaseURL } from '../../lib/appConfig'
|
||||
|
||||
type MetaTag = DetailedHTMLProps<
|
||||
@ -27,8 +26,6 @@ function openGraphType(ogImage: string | null): string {
|
||||
}
|
||||
|
||||
export function PageMetaData(props: PageMetaDataProps): JSX.Element {
|
||||
const isDarkMode = useDarkModeListener()
|
||||
|
||||
return (
|
||||
<Head>
|
||||
<link rel="icon" href="/static/icons/favicon.ico" sizes="32x32" />
|
||||
|
||||
62
packages/web/components/templates/article/AISummary.tsx
Normal file
62
packages/web/components/templates/article/AISummary.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { useGetAISummary } from '../../../lib/networking/queries/useGetAISummary'
|
||||
import { SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { AIPromptIcon } from '../../elements/icons/AIPromotIcon'
|
||||
|
||||
type AISummaryProps = {
|
||||
idx: string
|
||||
libraryItemId: string
|
||||
|
||||
fontFamily: string
|
||||
fontSize: number
|
||||
lineHeight: number
|
||||
readerFontColor: string
|
||||
}
|
||||
|
||||
export const AISummary = (props: AISummaryProps): JSX.Element => {
|
||||
const aisummary = useGetAISummary({
|
||||
idx: props.idx,
|
||||
libraryItemId: props.libraryItemId,
|
||||
})
|
||||
|
||||
if (!aisummary.summary) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<VStack
|
||||
css={{
|
||||
p: '20px',
|
||||
mx: '50px',
|
||||
mt: '50px',
|
||||
mb: '50px',
|
||||
gap: '15px',
|
||||
border: '1px solid $thLibraryAISummaryBorder',
|
||||
color: props.readerFontColor,
|
||||
fontFamily: props.fontFamily,
|
||||
fontSize: `${props.fontSize - 2}px`,
|
||||
lineHeight: `${props.lineHeight}%`,
|
||||
borderRadius: '3px',
|
||||
background: '$thLibraryAISummaryBackground',
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
px: '11px',
|
||||
bg: '#D0A3FF10',
|
||||
border: '1px solid #D0A3FF30',
|
||||
borderRadius: '4px',
|
||||
color: '#D0A3FF',
|
||||
fontSize: '12px',
|
||||
|
||||
fontFamily: '$inter',
|
||||
}}
|
||||
>
|
||||
AI Summary
|
||||
</SpanBox>
|
||||
<SpanBox>{aisummary.summary}</SpanBox>
|
||||
<SpanBox css={{ ml: 'auto' }}>
|
||||
<AIPromptIcon />
|
||||
</SpanBox>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
@ -19,6 +19,8 @@ import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import { Recommendation } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { Avatar } from '../../elements/Avatar'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { AISummary } from './AISummary'
|
||||
import { userHasFeature } from '../../../lib/featureFlag'
|
||||
|
||||
type ArticleContainerProps = {
|
||||
viewer: UserBasicData
|
||||
@ -444,6 +446,16 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
||||
recommendationsWithNotes={recommendationsWithNotes}
|
||||
/>
|
||||
)}
|
||||
{userHasFeature(props.viewer, 'ai-summaries') && (
|
||||
<AISummary
|
||||
libraryItemId={props.article.id}
|
||||
idx="latest"
|
||||
fontFamily={styles.fontFamily}
|
||||
fontSize={styles.fontSize}
|
||||
lineHeight={styles.lineHeight}
|
||||
readerFontColor={styles.readerFontColor}
|
||||
/>
|
||||
)}
|
||||
</VStack>
|
||||
<Article
|
||||
articleId={props.article.id}
|
||||
|
||||
@ -52,9 +52,10 @@ import { PinnedSearch } from '../../../pages/settings/pinned-searches'
|
||||
import { ErrorSlothIcon } from '../../elements/icons/ErrorSlothIcon'
|
||||
import { DEFAULT_HEADER_HEIGHT } from './HeaderSpacer'
|
||||
import { FetchItemsError } from './FetchItemsError'
|
||||
import { TLDRLayout } from './TLDRLayout'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights'
|
||||
export type LibraryMode = 'reads' | 'highlights' | 'tldr'
|
||||
|
||||
const fetchSearchResults = async (query: string, cb: any) => {
|
||||
if (!query.startsWith('#')) return
|
||||
@ -140,11 +141,17 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
}, [queryValue])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('ueryInputs.searchQuery', queryInputs.searchQuery)
|
||||
if (
|
||||
queryInputs.searchQuery &&
|
||||
queryInputs.searchQuery?.indexOf('mode:highlights') > -1
|
||||
) {
|
||||
setMode('highlights')
|
||||
} else if (
|
||||
queryInputs.searchQuery &&
|
||||
queryInputs.searchQuery?.indexOf('mode:tldr') > -1
|
||||
) {
|
||||
setMode('tldr')
|
||||
} else {
|
||||
setMode('reads')
|
||||
}
|
||||
@ -218,7 +225,6 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
const updatedArticle = { ...item }
|
||||
updatedArticle.node = { ...item.node }
|
||||
updatedArticle.isLoading = false
|
||||
console.log(`Updating Metadata of ${item.node.slug}.`)
|
||||
performActionOnItem('update-item', updatedArticle)
|
||||
return
|
||||
}
|
||||
@ -975,17 +981,20 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
||||
<VStack
|
||||
css={{
|
||||
height: '100%',
|
||||
width: props.mode == 'highlights' ? '100%' : 'unset',
|
||||
width: !showItems || props.mode == 'highlights' ? '100%' : 'unset',
|
||||
}}
|
||||
>
|
||||
{props.mode != 'highlights' && (
|
||||
<LibraryHeader
|
||||
layout={layout}
|
||||
viewer={viewerData?.me}
|
||||
updateLayout={updateLayout}
|
||||
searchTerm={props.searchTerm}
|
||||
applySearchQuery={(searchQuery: string) => {
|
||||
props.applySearchQuery(searchQuery)
|
||||
}}
|
||||
mode={props.mode}
|
||||
setMode={props.setMode}
|
||||
showFilterMenu={showFilterMenu}
|
||||
setShowFilterMenu={setShowFilterMenu}
|
||||
multiSelectMode={props.multiSelectMode}
|
||||
@ -1048,6 +1057,10 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showItems && props.mode == 'tldr' && (
|
||||
<TLDRLayout viewer={viewerData?.me} layout={layout} {...props} />
|
||||
)}
|
||||
|
||||
{props.showAddLinkModal && (
|
||||
<AddLinkModal
|
||||
handleLinkSubmission={props.handleLinkSubmission}
|
||||
|
||||
@ -5,26 +5,24 @@ import { FormInput } from '../../elements/FormElements'
|
||||
import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import { Button, IconButton } from '../../elements/Button'
|
||||
import { FunnelSimple, MagnifyingGlass, Plus, X } from 'phosphor-react'
|
||||
import { LayoutType } from './HomeFeedContainer'
|
||||
import { FunnelSimple, X } from 'phosphor-react'
|
||||
import { LayoutType, LibraryMode } from './HomeFeedContainer'
|
||||
import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo'
|
||||
import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from './HeaderSpacer'
|
||||
import { LIBRARY_LEFT_MENU_WIDTH } from '../navMenu/LibraryMenu'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { AddBulkLabelsModal } from '../article/AddBulkLabelsModal'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
import { TrashIcon } from '../../elements/icons/TrashIcon'
|
||||
import { LabelIcon } from '../../elements/icons/LabelIcon'
|
||||
import { HeaderCheckboxIcon } from '../../elements/icons/HeaderCheckboxIcon'
|
||||
import { HeaderSearchIcon } from '../../elements/icons/HeaderSearchIcon'
|
||||
import { HeaderToggleGridIcon } from '../../elements/icons/HeaderToggleGridIcon'
|
||||
import { HeaderToggleListIcon } from '../../elements/icons/HeaderToggleListIcon'
|
||||
import { HeaderToggleTLDRIcon } from '../../elements/icons/HeaderToggleTLDRIcon'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { userHasFeature } from '../../../lib/featureFlag'
|
||||
import { MultiSelectControls, CheckBoxButton } from './MultiSelectControls'
|
||||
|
||||
export type MultiSelectMode = 'off' | 'none' | 'some' | 'visible' | 'search'
|
||||
|
||||
type LibraryHeaderProps = {
|
||||
export type LibraryHeaderProps = {
|
||||
viewer: UserBasicData | undefined
|
||||
|
||||
layout: LayoutType
|
||||
updateLayout: (layout: LayoutType) => void
|
||||
|
||||
@ -34,6 +32,9 @@ type LibraryHeaderProps = {
|
||||
showFilterMenu: boolean
|
||||
setShowFilterMenu: (show: boolean) => void
|
||||
|
||||
mode: LibraryMode
|
||||
setMode: (set: LibraryMode) => void
|
||||
|
||||
numItemsSelected: number
|
||||
multiSelectMode: MultiSelectMode
|
||||
setMultiSelectMode: (mode: MultiSelectMode) => void
|
||||
@ -81,18 +82,21 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<VStack
|
||||
alignment="center"
|
||||
alignment="start"
|
||||
distribution="start"
|
||||
css={{
|
||||
top: '0',
|
||||
right: '0',
|
||||
zIndex: 5,
|
||||
px: '70px',
|
||||
bg: '$thLibraryBackground',
|
||||
position: 'fixed',
|
||||
left: LIBRARY_LEFT_MENU_WIDTH,
|
||||
height: small ? '60px' : DEFAULT_HEADER_HEIGHT,
|
||||
transition: 'height 0.5s',
|
||||
'@lgDown': { px: '20px' },
|
||||
'@mdDown': {
|
||||
px: '10px',
|
||||
left: '0px',
|
||||
right: '0',
|
||||
},
|
||||
@ -121,7 +125,6 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element {
|
||||
>
|
||||
{props.multiSelectMode !== 'off' ? (
|
||||
<>
|
||||
<CheckBoxButton {...props} />
|
||||
<MultiSelectControls {...props} />
|
||||
</>
|
||||
) : (
|
||||
@ -131,33 +134,7 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
const CheckBoxButton = (props: LibraryHeaderProps): JSX.Element => {
|
||||
return (
|
||||
<Button
|
||||
title="Select multiple"
|
||||
style="plainIcon"
|
||||
css={{ display: 'flex', '&:hover': { opacity: '1.0' } }}
|
||||
onClick={(e) => {
|
||||
switch (props.multiSelectMode) {
|
||||
case 'off':
|
||||
case 'none':
|
||||
case 'some':
|
||||
props.setMultiSelectMode('visible')
|
||||
break
|
||||
default:
|
||||
props.setMultiSelectMode('off')
|
||||
break
|
||||
}
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HeaderCheckboxIcon multiSelectMode={props.multiSelectMode} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
const HeaderControls = (props: LibraryHeaderProps): JSX.Element => {
|
||||
const [showSearchBar, setShowSearchBar] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<SpanBox
|
||||
@ -168,49 +145,58 @@ const HeaderControls = (props: LibraryHeaderProps): JSX.Element => {
|
||||
>
|
||||
<MenuHeaderButton {...props} />
|
||||
</SpanBox>
|
||||
<CheckBoxButton {...props} />
|
||||
|
||||
{showSearchBar ? (
|
||||
<SearchBox {...props} setShowSearchBar={setShowSearchBar} />
|
||||
) : (
|
||||
<SearchBox {...props} />
|
||||
|
||||
<SpanBox css={{ display: 'flex', ml: 'auto', gap: '10px' }}>
|
||||
{userHasFeature(props.viewer, 'ai-summaries') && (
|
||||
<Button
|
||||
title="TLDR Summaries"
|
||||
style="plainIcon"
|
||||
css={{
|
||||
display: 'flex',
|
||||
marginLeft: 'auto',
|
||||
'&:hover': { opacity: '1.0' },
|
||||
}}
|
||||
onClick={(e) => {
|
||||
if (props.mode == 'reads') {
|
||||
props.setMode('tldr')
|
||||
} else {
|
||||
props.setMode('reads')
|
||||
}
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HeaderToggleTLDRIcon />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
title="search"
|
||||
title={
|
||||
props.layout == 'GRID_LAYOUT'
|
||||
? 'Switch to list layout'
|
||||
: 'Switch to grid layout'
|
||||
}
|
||||
style="plainIcon"
|
||||
css={{ display: 'flex', '&:hover': { opacity: '1.0' } }}
|
||||
css={{
|
||||
display: 'flex',
|
||||
marginLeft: 'auto',
|
||||
'&:hover': { opacity: '1.0' },
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setShowSearchBar(true)
|
||||
props.updateLayout(
|
||||
props.layout == 'GRID_LAYOUT' ? 'LIST_LAYOUT' : 'GRID_LAYOUT'
|
||||
)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HeaderSearchIcon />
|
||||
{props.layout == 'LIST_LAYOUT' ? (
|
||||
<HeaderToggleGridIcon />
|
||||
) : (
|
||||
<HeaderToggleListIcon />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
title={
|
||||
props.layout == 'GRID_LAYOUT'
|
||||
? 'Switch to list layout'
|
||||
: 'Switch to grid layout'
|
||||
}
|
||||
style="plainIcon"
|
||||
css={{
|
||||
display: 'flex',
|
||||
marginLeft: 'auto',
|
||||
'&:hover': { opacity: '1.0' },
|
||||
}}
|
||||
onClick={(e) => {
|
||||
props.updateLayout(
|
||||
props.layout == 'GRID_LAYOUT' ? 'LIST_LAYOUT' : 'GRID_LAYOUT'
|
||||
)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
{props.layout == 'LIST_LAYOUT' ? (
|
||||
<HeaderToggleGridIcon />
|
||||
) : (
|
||||
<HeaderToggleListIcon />
|
||||
)}
|
||||
</Button>
|
||||
</SpanBox>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -257,31 +243,15 @@ export function MenuHeaderButton(props: MenuHeaderButtonProps): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
export type SearchBoxProps = {
|
||||
searchTerm: string | undefined
|
||||
applySearchQuery: (searchQuery: string) => void
|
||||
setShowSearchBar: (show: boolean) => void
|
||||
|
||||
compact?: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
export function SearchBox(props: LibraryHeaderProps): JSX.Element {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null)
|
||||
const [focused, setFocused] = useState(false)
|
||||
const [searchTerm, setSearchTerm] = useState(props.searchTerm ?? '')
|
||||
const [isAddAction, setIsAddAction] = useState(false)
|
||||
const IS_URL_REGEX =
|
||||
/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/
|
||||
|
||||
useEffect(() => {
|
||||
setSearchTerm(props.searchTerm ?? '')
|
||||
}, [props.searchTerm])
|
||||
|
||||
useEffect(() => {
|
||||
setIsAddAction(IS_URL_REGEX.test(searchTerm))
|
||||
}, [searchTerm, props.searchTerm])
|
||||
|
||||
useKeyboardShortcuts(
|
||||
searchBarCommands((action) => {
|
||||
if (action === 'focusSearchBar' && inputRef.current) {
|
||||
@ -301,10 +271,7 @@ export function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
width: '100%',
|
||||
maxWidth: '521px',
|
||||
bg: '$thLibrarySearchbox',
|
||||
borderRadius: '100px',
|
||||
border: focused
|
||||
? '2px solid $searchActiveOutline'
|
||||
: '2px solid transparent',
|
||||
borderRadius: '6px',
|
||||
boxShadow: focused
|
||||
? 'none'
|
||||
: '0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);',
|
||||
@ -317,251 +284,126 @@ export function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
distribution="center"
|
||||
css={{
|
||||
width: '53px',
|
||||
height: '100%',
|
||||
pl: props.compact ? '10px' : '15px',
|
||||
pr: props.compact ? '5px' : '10px',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
inputRef.current?.focus()
|
||||
e.preventDefault()
|
||||
display: 'flex',
|
||||
bg: props.multiSelectMode !== 'off' ? '$ctaBlue' : 'transparent',
|
||||
borderTopLeftRadius: '6px',
|
||||
borderBottomLeftRadius: '6px',
|
||||
'--checkbox-color': 'var(--colors-thLibraryMultiselectCheckbox)',
|
||||
'&:hover': {
|
||||
bg: '$thLibraryMultiselectHover',
|
||||
'--checkbox-color':
|
||||
'var(--colors-thLibraryMultiselectCheckboxHover)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{(() => {
|
||||
if (isAddAction) {
|
||||
return (
|
||||
<Plus
|
||||
size={props.compact ? 15 : 20}
|
||||
color={theme.colors.graySolid.toString()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<MagnifyingGlass
|
||||
size={props.compact ? 15 : 20}
|
||||
color={theme.colors.graySolid.toString()}
|
||||
/>
|
||||
)
|
||||
})()}
|
||||
<CheckBoxButton {...props} />
|
||||
</HStack>
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
event.preventDefault()
|
||||
props.applySearchQuery(searchTerm || '')
|
||||
inputRef.current?.blur()
|
||||
if (props.onClose) {
|
||||
props.onClose()
|
||||
}
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<FormInput
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
autoFocus={true}
|
||||
placeholder="Search keywords or labels"
|
||||
onFocus={(event) => {
|
||||
event.target.select()
|
||||
setFocused(true)
|
||||
}}
|
||||
onBlur={() => {
|
||||
setFocused(false)
|
||||
}}
|
||||
onChange={(event) => {
|
||||
setSearchTerm(event.target.value)
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
const key = event.key.toLowerCase()
|
||||
if (key == 'escape') {
|
||||
event.currentTarget.blur()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
css={{
|
||||
py: '15px',
|
||||
marginLeft: 'auto',
|
||||
border: focused
|
||||
? '2px solid $searchActiveOutline'
|
||||
: '2px solid transparent',
|
||||
borderTopRightRadius: '6px',
|
||||
borderBottomRightRadius: '6px',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
{/* <Button
|
||||
css={{ padding: '4px', borderRadius: '50px', fontSize: '10px' }}
|
||||
onClick={(event) => {
|
||||
if (searchTerm && searchTerm.length) {
|
||||
event.preventDefault()
|
||||
setSearchTerm('')
|
||||
props.applySearchQuery('')
|
||||
} else {
|
||||
props.setShowSearchBar(false)
|
||||
}
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
event.preventDefault()
|
||||
props.applySearchQuery(searchTerm || '')
|
||||
inputRef.current?.blur()
|
||||
}}
|
||||
tabIndex={-1}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
clear
|
||||
</Button> */}
|
||||
<IconButton
|
||||
style="searchButton"
|
||||
onClick={(event) => {
|
||||
if (searchTerm && searchTerm.length && searchTerm != 'in:inbox') {
|
||||
event.preventDefault()
|
||||
<FormInput
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
autoFocus={false}
|
||||
placeholder="Search keywords or labels"
|
||||
onFocus={(event) => {
|
||||
event.target.select()
|
||||
setFocused(true)
|
||||
}}
|
||||
onBlur={() => {
|
||||
setFocused(false)
|
||||
}}
|
||||
onChange={(event) => {
|
||||
setSearchTerm(event.target.value)
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
const key = event.key.toLowerCase()
|
||||
if (key == 'escape') {
|
||||
event.currentTarget.blur()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
<HStack
|
||||
alignment="center"
|
||||
css={{
|
||||
py: '15px',
|
||||
mr: '10px',
|
||||
marginLeft: 'auto',
|
||||
}}
|
||||
>
|
||||
<CancelSearchButton
|
||||
onClick={() => {
|
||||
setSearchTerm('in:inbox')
|
||||
props.applySearchQuery('')
|
||||
} else {
|
||||
props.setShowSearchBar(false)
|
||||
}
|
||||
}}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<X
|
||||
width={16}
|
||||
height={16}
|
||||
color={theme.colors.grayTextContrast.toString()}
|
||||
inputRef.current?.blur()
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
type ControlButtonBoxProps = {
|
||||
layout: LayoutType
|
||||
updateLayout: (layout: LayoutType) => void
|
||||
setShowInlineSearch?: (show: boolean) => void
|
||||
|
||||
numItemsSelected: number
|
||||
multiSelectMode: MultiSelectMode
|
||||
setMultiSelectMode: (mode: MultiSelectMode) => void
|
||||
|
||||
performMultiSelectAction: (action: BulkAction, labelIds?: string[]) => void
|
||||
type CancelSearchButtonProps = {
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
function MultiSelectControls(props: ControlButtonBoxProps): JSX.Element {
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [showLabelsModal, setShowLabelsModal] = useState(false)
|
||||
const compact = false
|
||||
|
||||
const CancelSearchButton = (props: CancelSearchButtonProps): JSX.Element => {
|
||||
const [color, setColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Box
|
||||
<Button
|
||||
title="Cancel"
|
||||
style="plainIcon"
|
||||
css={{
|
||||
height: '38px',
|
||||
width: '100%',
|
||||
maxWidth: '521px',
|
||||
bg: '$thLibrarySearchbox',
|
||||
borderRadius: '100px',
|
||||
border: '2px solid transparent',
|
||||
boxShadow:
|
||||
'0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);',
|
||||
p: '5px',
|
||||
display: 'flex',
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
borderRadius: '100px',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
props.onClick()
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="end"
|
||||
css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
gap: '15px',
|
||||
pl: compact ? '10px' : '15px',
|
||||
pr: compact ? '5px' : '10px',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontSize: '14px',
|
||||
fontFamily: '$display',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
>
|
||||
{props.numItemsSelected} items selected
|
||||
</SpanBox>
|
||||
<Button
|
||||
title="Archive"
|
||||
css={{ display: 'flex' }}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
props.performMultiSelectAction(BulkAction.ARCHIVE)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<ArchiveIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
title="Add labels"
|
||||
css={{ display: 'flex' }}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
setShowLabelsModal(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<LabelIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
title="Delete"
|
||||
css={{ display: 'flex' }}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<TrashIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
{showConfirmDelete && (
|
||||
<ConfirmationModal
|
||||
message={`You are about to delete ${props.numItemsSelected} items. All associated notes and highlights will be deleted.`}
|
||||
acceptButtonLabel={'Delete'}
|
||||
onAccept={() => {
|
||||
props.performMultiSelectAction(BulkAction.DELETE)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showLabelsModal && (
|
||||
<AddBulkLabelsModal
|
||||
bulkSetLabels={(labels: Label[]) => {
|
||||
const labelIds = labels.map((l) => l.id)
|
||||
props.performMultiSelectAction(BulkAction.ADD_LABELS, labelIds)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowLabelsModal(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
title="Cancel"
|
||||
css={{ display: 'flex', mr: '10px' }}
|
||||
style="plainIcon"
|
||||
onClick={(event) => {
|
||||
props.setMultiSelectMode('off')
|
||||
}}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<X
|
||||
width={20}
|
||||
height={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
</HStack>
|
||||
</Box>
|
||||
<X width={19} height={19} color={color} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,301 @@
|
||||
import { useState } from 'react'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { Box, HStack, SpanBox } from '../../elements/LayoutPrimitives'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
import { LabelIcon } from '../../elements/icons/LabelIcon'
|
||||
import { TrashIcon } from '../../elements/icons/TrashIcon'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { AddBulkLabelsModal } from '../article/AddBulkLabelsModal'
|
||||
import { X } from 'phosphor-react'
|
||||
import { LibraryHeaderProps } from './LibraryHeader'
|
||||
import { HeaderCheckboxIcon } from '../../elements/icons/HeaderCheckboxIcon'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
|
||||
export const MultiSelectControls = (props: LibraryHeaderProps): JSX.Element => {
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [showLabelsModal, setShowLabelsModal] = useState(false)
|
||||
// Don't change on immediate hover, the button has to be blurred at least once
|
||||
const [hoveredOut, setHoveredOut] = useState(false)
|
||||
const [hoverColor, setHoverColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
const compact = false
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
height: '38px',
|
||||
width: '100%',
|
||||
maxWidth: '521px',
|
||||
bg: '$thLibrarySearchbox',
|
||||
borderRadius: '6px',
|
||||
boxShadow:
|
||||
'0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);',
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setHoveredOut(true)
|
||||
event.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="end"
|
||||
css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
pr: compact ? '5px' : '10px',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="center"
|
||||
css={{
|
||||
width: '53px',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
bg: props.multiSelectMode !== 'off' ? '$ctaBlue' : 'transparent',
|
||||
borderTopLeftRadius: '6px',
|
||||
borderBottomLeftRadius: '6px',
|
||||
'--checkbox-color': 'white',
|
||||
'&:hover': {
|
||||
bg: hoveredOut ? '$thLibraryMultiselectHover' : '$ctaBlue',
|
||||
'--checkbox-color': hoveredOut
|
||||
? 'var(--colors-thLibraryMultiselectCheckboxHover)'
|
||||
: 'white',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckBoxButton {...props} />
|
||||
</HStack>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
css={{
|
||||
gap: '15px',
|
||||
pl: '15px',
|
||||
border: '2px solid transparent',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontSize: '14px',
|
||||
fontFamily: '$display',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
>
|
||||
{props.numItemsSelected} items selected
|
||||
</SpanBox>
|
||||
<ArchiveButton {...props} />
|
||||
<AddLabelsButton setShowLabelsModal={setShowLabelsModal} />
|
||||
<RemoveItemsButton setShowConfirmDelete={setShowConfirmDelete} />
|
||||
{showConfirmDelete && (
|
||||
<ConfirmationModal
|
||||
message={`You are about to delete ${props.numItemsSelected} items. All associated notes and highlights will be deleted.`}
|
||||
acceptButtonLabel={'Delete'}
|
||||
onAccept={() => {
|
||||
props.performMultiSelectAction(BulkAction.DELETE)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showLabelsModal && (
|
||||
<AddBulkLabelsModal
|
||||
bulkSetLabels={(labels: Label[]) => {
|
||||
const labelIds = labels.map((l) => l.id)
|
||||
props.performMultiSelectAction(BulkAction.ADD_LABELS, labelIds)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowLabelsModal(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<CancelButton {...props} />
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export const CheckBoxButton = (props: LibraryHeaderProps): JSX.Element => {
|
||||
return (
|
||||
<Button
|
||||
title="Select multiple"
|
||||
style="plainIcon"
|
||||
css={{ display: 'flex', '&:hover': { opacity: '1.0' } }}
|
||||
onClick={(e) => {
|
||||
switch (props.multiSelectMode) {
|
||||
case 'off':
|
||||
case 'none':
|
||||
case 'some':
|
||||
props.setMultiSelectMode('visible')
|
||||
break
|
||||
default:
|
||||
props.setMultiSelectMode('off')
|
||||
break
|
||||
}
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HeaderCheckboxIcon multiSelectMode={props.multiSelectMode} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export const ArchiveButton = (props: LibraryHeaderProps): JSX.Element => {
|
||||
const [color, setColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Archive"
|
||||
css={{
|
||||
p: '5px',
|
||||
display: 'flex',
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
borderRadius: '100px',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
props.performMultiSelectAction(BulkAction.ARCHIVE)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<ArchiveIcon size={20} color={color} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
type AddLabelsButtonProps = {
|
||||
setShowLabelsModal: (set: boolean) => void
|
||||
}
|
||||
|
||||
export const AddLabelsButton = (props: AddLabelsButtonProps): JSX.Element => {
|
||||
const [color, setColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Add labels"
|
||||
css={{
|
||||
p: '5px',
|
||||
display: 'flex',
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
borderRadius: '100px',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
props.setShowLabelsModal(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<LabelIcon size={20} color={color} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
type RemoveItemsButtonProps = {
|
||||
setShowConfirmDelete: (set: boolean) => void
|
||||
}
|
||||
|
||||
export const RemoveItemsButton = (
|
||||
props: RemoveItemsButtonProps
|
||||
): JSX.Element => {
|
||||
const [color, setColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Remove"
|
||||
css={{
|
||||
p: '5px',
|
||||
display: 'flex',
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
borderRadius: '100px',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
props.setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<TrashIcon size={20} color={color} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export const CancelButton = (props: LibraryHeaderProps): JSX.Element => {
|
||||
const [color, setColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Cancel"
|
||||
css={{
|
||||
p: '5px',
|
||||
display: 'flex',
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
borderRadius: '100px',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
props.setMultiSelectMode('off')
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<X width={19} height={19} color={color} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
296
packages/web/components/templates/homeFeed/TLDRLayout.tsx
Normal file
296
packages/web/components/templates/homeFeed/TLDRLayout.tsx
Normal file
@ -0,0 +1,296 @@
|
||||
import { LayoutType } from './HomeFeedContainer'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { LibraryItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
import { TrashIcon } from '../../elements/icons/TrashIcon'
|
||||
import { BrowserIcon } from '../../elements/icons/BrowserIcon'
|
||||
import { styled } from '@stitches/react'
|
||||
import { siteName } from '../../patterns/LibraryCards/LibraryCardStyles'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { DotsThree } from 'phosphor-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
type TLDRLayoutProps = {
|
||||
layout: LayoutType
|
||||
viewer?: UserBasicData
|
||||
|
||||
items: LibraryItem[]
|
||||
isValidating: boolean
|
||||
|
||||
hasMore: boolean
|
||||
totalItems: number
|
||||
|
||||
loadMore: () => void
|
||||
}
|
||||
|
||||
const SiteIcon = styled('img', {
|
||||
width: '22px',
|
||||
height: '22px',
|
||||
borderRadius: '100px',
|
||||
})
|
||||
|
||||
export function TLDRLayout(props: TLDRLayoutProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<VStack
|
||||
alignment="start"
|
||||
distribution="start"
|
||||
css={{
|
||||
mt: '30px',
|
||||
gap: '50px',
|
||||
height: '100%',
|
||||
minHeight: '100vh',
|
||||
px: '0px',
|
||||
width: '100%',
|
||||
maxWidth: '800px',
|
||||
'@mdDown': {
|
||||
p: '10px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Toaster />
|
||||
|
||||
{props.isValidating && props.items.length == 0 && <TopBarProgress />}
|
||||
|
||||
{props.items.map((item) => {
|
||||
const source = siteName(
|
||||
item.node.originalArticleUrl,
|
||||
item.node.url,
|
||||
item.node.siteName
|
||||
)
|
||||
return (
|
||||
<VStack key={`tldr-${item.node.id}`} css={{ gap: '10px' }}>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
css={{
|
||||
gap: '5px',
|
||||
width: '100%',
|
||||
height: '25px',
|
||||
pb: 'red',
|
||||
}}
|
||||
>
|
||||
<VStack
|
||||
distribution="center"
|
||||
alignment="center"
|
||||
css={{
|
||||
mr: '5px',
|
||||
display: 'flex',
|
||||
w: '22px',
|
||||
h: '22px',
|
||||
borderRadius: '100px',
|
||||
bg: '$ctaBlue',
|
||||
color: '$thTextSubtle',
|
||||
}}
|
||||
>
|
||||
<SiteIcon src={item.node.siteIcon} />
|
||||
</VStack>
|
||||
{source && (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
fontFamily: '$inter',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
{item.node.siteName}
|
||||
</SpanBox>
|
||||
)}
|
||||
{source && item.node.author && (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
fontFamily: '$inter',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
•
|
||||
</SpanBox>
|
||||
)}
|
||||
{item.node.author && (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
fontFamily: '$inter',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
{item.node.author}
|
||||
</SpanBox>
|
||||
)}
|
||||
<SpanBox css={{ ml: 'auto' }}>
|
||||
<DotsThree
|
||||
size={20}
|
||||
color={theme.colors.thTextSubtle.toString()}
|
||||
/>
|
||||
</SpanBox>
|
||||
</HStack>
|
||||
<HStack
|
||||
css={{
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<VStack
|
||||
alignment="start"
|
||||
distribution="start"
|
||||
css={{ gap: '10px' }}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '700',
|
||||
fontSize: '20px',
|
||||
textDecoration: 'underline',
|
||||
a: {
|
||||
color: '$thTLDRText',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<a href={``}>{item.node.title}</a>
|
||||
</SpanBox>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '500',
|
||||
fontSize: '14px',
|
||||
lineHeight: '30px',
|
||||
color: '$thTLDRText',
|
||||
}}
|
||||
>
|
||||
{item.node.aiSummary}
|
||||
</SpanBox>
|
||||
<HStack css={{ gap: '15px', pt: '5px' }}>
|
||||
<ArchiveButton />
|
||||
<RemoveButton />
|
||||
<OpenOriginalButton />
|
||||
</HStack>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
)
|
||||
})}
|
||||
|
||||
<HStack
|
||||
distribution="center"
|
||||
css={{ width: '100%', mt: '$2', mb: '$4' }}
|
||||
>
|
||||
{props.hasMore ? (
|
||||
<Button
|
||||
style="ctaGray"
|
||||
css={{
|
||||
cursor: props.isValidating ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
onClick={props.loadMore}
|
||||
disabled={props.isValidating}
|
||||
>
|
||||
{props.isValidating ? 'Loading' : 'Load More'}
|
||||
</Button>
|
||||
) : (
|
||||
<StyledText style="caption"></StyledText>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ArchiveButton = (): JSX.Element => {
|
||||
const [foreground, setForegroundColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Archive"
|
||||
style="tldr"
|
||||
css={{
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setForegroundColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setForegroundColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
onClick={(e) => {
|
||||
// props.setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<ArchiveIcon size={20} color={foreground} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
const RemoveButton = (): JSX.Element => {
|
||||
const [foreground, setForegroundColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Remove"
|
||||
style="tldr"
|
||||
css={{
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setForegroundColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setForegroundColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
onClick={(e) => {
|
||||
// props.setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<TrashIcon size={20} color={foreground} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
const OpenOriginalButton = (): JSX.Element => {
|
||||
const [foreground, setForegroundColor] = useState<string>(
|
||||
theme.colors.thTextContrast2.toString()
|
||||
)
|
||||
return (
|
||||
<Button
|
||||
title="Open original"
|
||||
style="tldr"
|
||||
css={{
|
||||
'&:hover': {
|
||||
bg: '$ctaBlue',
|
||||
opacity: 1.0,
|
||||
},
|
||||
}}
|
||||
onMouseEnter={(event) => {
|
||||
setForegroundColor('white')
|
||||
event.preventDefault()
|
||||
}}
|
||||
onMouseLeave={(event) => {
|
||||
setForegroundColor(theme.colors.thTextContrast2.toString())
|
||||
event.preventDefault()
|
||||
}}
|
||||
onClick={(e) => {
|
||||
// props.setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<BrowserIcon size={20} color={foreground} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@ -27,10 +27,7 @@ import { HighlightsIcon } from '../../elements/icons/HighlightsIcon'
|
||||
import { CoverImage } from '../../elements/CoverImage'
|
||||
import { Shortcut } from '../../../pages/settings/shortcuts'
|
||||
import { OutlinedLabelChip } from '../../elements/OutlinedLabelChip'
|
||||
import { NewsletterFlairIcon } from '../../elements/icons/NewsletterFlairIcon'
|
||||
import { FeedFlairIcon } from '../../elements/icons/FeedFlairIcon'
|
||||
import { NewsletterIcon } from '../../elements/icons/NewsletterIcon'
|
||||
import { DropdownMenu } from '@radix-ui/react-dropdown-menu'
|
||||
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { DropdownMenu } from '@radix-ui/react-dropdown-menu'
|
||||
import { SETTINGS_SECTION_1, SETTINGS_SECTION_2 } from './SettingsMenu'
|
||||
import {
|
||||
Dropdown,
|
||||
|
||||
@ -16,7 +16,7 @@ export const SETTINGS_SECTION_1 = [
|
||||
{ name: 'Feeds', destination: '/settings/feeds' },
|
||||
{ name: 'Subscriptions', destination: '/settings/subscriptions' },
|
||||
{ name: 'Labels', destination: '/settings/labels' },
|
||||
{ name: 'Shortcuts', destination: '/settings/shortcuts' },
|
||||
// { name: 'Shortcuts', destination: '/settings/shortcuts' },
|
||||
{ name: 'Saved Searches', destination: '/settings/saved-searches' },
|
||||
{ name: 'Pinned Searches', destination: '/settings/pinned-searches' },
|
||||
]
|
||||
|
||||
@ -185,10 +185,16 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
||||
thLibrarySelectionColor: '#FFEA9F',
|
||||
thLibraryNavigationMenuFooter: '#EFEADE',
|
||||
thLibraryMenuFooterHover: '#FFFFFF',
|
||||
thLibraryMultiselectHover: '#D9D9D9',
|
||||
thLibraryMultiselectCheckbox: '#3D3D3D',
|
||||
thLibraryMultiselectCheckboxHover: '#3D3D3D',
|
||||
|
||||
thTLDRText: '#434343',
|
||||
|
||||
thFormInput: '#EBEBEB',
|
||||
thHomeIcon: '#2A2A2A',
|
||||
|
||||
thLabelChipForeground: '#2A2A2A', // : '#2A2A2A'
|
||||
thLabelChipForeground: '#2A2A2A',
|
||||
thLabelChipBackground: '#F5F5F5',
|
||||
thLabelChipSelectedBorder: 'black',
|
||||
thLabelChipUnselectedBorder: '#F5F5F5',
|
||||
@ -218,6 +224,9 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
||||
thHighContrast: '#3D3D3D',
|
||||
thHighlightBar: '#D9D9D9',
|
||||
|
||||
thLibraryAISummaryBorder: '#6A6968',
|
||||
thLibraryAISummaryBackground: '#343434',
|
||||
|
||||
thFallbackImageForeground: '#2A2A2A',
|
||||
thFallbackImageBackground: '#EDEDED',
|
||||
|
||||
@ -316,6 +325,12 @@ const darkThemeSpec = {
|
||||
thLibrarySelectionColor: '#6A6968',
|
||||
thLibraryNavigationMenuFooter: '#3D3D3D',
|
||||
thLibraryMenuFooterHover: '#6A6968',
|
||||
thLibraryMultiselectHover: '#6A6968',
|
||||
thLibraryMultiselectCheckbox: 'white',
|
||||
thLibraryMultiselectCheckboxHover: 'white',
|
||||
|
||||
thTLDRText: '#D9D9D9',
|
||||
|
||||
searchActiveOutline: '#866D15',
|
||||
thFormInput: '#3D3D3D',
|
||||
thHomeIcon: '#FFFFFF',
|
||||
@ -352,6 +367,9 @@ const darkThemeSpec = {
|
||||
|
||||
thHighlightBar: '#6A6968',
|
||||
|
||||
thLibraryAISummaryBorder: '#6A6968',
|
||||
thLibraryAISummaryBackground: '#343434',
|
||||
|
||||
thFallbackImageForeground: '#FEFFFF',
|
||||
thFallbackImageBackground: '#3C3C3C',
|
||||
|
||||
|
||||
@ -1,7 +1,17 @@
|
||||
import { UserBasicData } from "./networking/queries/useGetViewerQuery"
|
||||
import { UserBasicData } from './networking/queries/useGetViewerQuery'
|
||||
|
||||
const VIP_USERS = ['jacksonh', 'satindar', 'hongbo', 'nat']
|
||||
|
||||
export const isVipUser = (user: UserBasicData): boolean => {
|
||||
return VIP_USERS.includes(user.profile.username)
|
||||
}
|
||||
|
||||
export const userHasFeature = (
|
||||
user: UserBasicData | undefined,
|
||||
feature: string
|
||||
): boolean => {
|
||||
if (!user) {
|
||||
return false
|
||||
}
|
||||
return user.features.includes(feature)
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ import { Label } from '../networking/fragments/labelFragment'
|
||||
import { showErrorToast } from '../toastHelpers'
|
||||
import { setLabelsForHighlight } from '../networking/mutations/setLabelsForHighlight'
|
||||
import { LabelsDispatcher } from './useSetPageLabels'
|
||||
import { Highlight } from '../networking/fragments/highlightFragment'
|
||||
|
||||
export const useSetHighlightLabels = (
|
||||
highlightId?: string
|
||||
|
||||
@ -60,6 +60,17 @@ export function gqlFetcher(
|
||||
return graphQLClient.request(query, variables, requestHeaders())
|
||||
}
|
||||
|
||||
export function apiFetcher(path: string): Promise<unknown> {
|
||||
const url = new URL(path, fetchEndpoint)
|
||||
return fetch(url.toString(), {
|
||||
headers: requestHeaders(),
|
||||
credentials: 'include',
|
||||
mode: 'cors',
|
||||
}).then((result) => {
|
||||
return result.json()
|
||||
})
|
||||
}
|
||||
|
||||
export function makePublicGqlFetcher(
|
||||
variables?: unknown
|
||||
): (query: string) => Promise<unknown> {
|
||||
|
||||
41
packages/web/lib/networking/queries/useGetAISummary.tsx
Normal file
41
packages/web/lib/networking/queries/useGetAISummary.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import useSWR, { Fetcher } from 'swr'
|
||||
import { apiFetcher } from '../networkHelpers'
|
||||
|
||||
export interface AISummary {
|
||||
id: string
|
||||
summary: string
|
||||
}
|
||||
|
||||
export interface AISummaryResponse {
|
||||
error: any
|
||||
isValidating: boolean
|
||||
summary: string | undefined
|
||||
}
|
||||
|
||||
export function useGetAISummary(params: {
|
||||
idx: string
|
||||
libraryItemId: string
|
||||
}): AISummaryResponse {
|
||||
const { idx, libraryItemId } = params
|
||||
const { data, error, isValidating } = useSWR(
|
||||
`/api/ai-summary/library-item/${libraryItemId}/${idx}`,
|
||||
apiFetcher
|
||||
)
|
||||
|
||||
try {
|
||||
const result = data as AISummary
|
||||
console.log('ai summary result: ', result)
|
||||
return {
|
||||
error,
|
||||
isValidating,
|
||||
summary: result.summary,
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
return {
|
||||
error,
|
||||
isValidating: false,
|
||||
summary: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,10 +95,12 @@ export type LibraryItemNode = {
|
||||
state: State
|
||||
pageType: PageType
|
||||
siteName?: string
|
||||
siteIcon?: string
|
||||
subscription?: string
|
||||
readAt?: string
|
||||
savedAt?: string
|
||||
wordsCount?: number
|
||||
aiSummary?: string
|
||||
recommendations?: Recommendation[]
|
||||
highlights?: Highlight[]
|
||||
}
|
||||
@ -172,6 +174,7 @@ export function useGetLibraryItemsQuery({
|
||||
ownedByViewer
|
||||
originalArticleUrl
|
||||
uploadFileId
|
||||
aiSummary
|
||||
labels {
|
||||
id
|
||||
name
|
||||
@ -183,6 +186,7 @@ export function useGetLibraryItemsQuery({
|
||||
annotation
|
||||
state
|
||||
siteName
|
||||
siteIcon
|
||||
subscription
|
||||
readAt
|
||||
savedAt
|
||||
|
||||
@ -20,6 +20,7 @@ export type UserBasicData = {
|
||||
email: string
|
||||
source: string
|
||||
intercomHash: string
|
||||
features: string[]
|
||||
}
|
||||
|
||||
export type UserProfile = {
|
||||
@ -45,6 +46,7 @@ export function useGetViewerQuery(): ViewerQueryResponse {
|
||||
email
|
||||
source
|
||||
intercomHash
|
||||
features
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -16,9 +16,14 @@ function LoadedContent(): JSX.Element {
|
||||
pageTestId="home-page-tag"
|
||||
>
|
||||
<VStack
|
||||
alignment="center"
|
||||
alignment="start"
|
||||
distribution="center"
|
||||
css={{ backgroundColor: '$thLibraryBackground' }}
|
||||
css={{
|
||||
px: '70px',
|
||||
backgroundColor: '$thLibraryBackground',
|
||||
'@lgDown': { px: '20px' },
|
||||
'@mdDown': { px: '10px' },
|
||||
}}
|
||||
>
|
||||
<HomeFeedContainer />
|
||||
</VStack>
|
||||
|
||||
@ -406,6 +406,49 @@ export default function Account(): JSX.Element {
|
||||
{/* <Button style="ctaDarkYellow">Upgrade</Button> */}
|
||||
</VStack>
|
||||
|
||||
<VStack
|
||||
css={{
|
||||
padding: '24px',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
bg: '$grayBg',
|
||||
gap: '10px',
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
>
|
||||
<StyledLabel>Beta features</StyledLabel>
|
||||
{!isValidating && (
|
||||
<>
|
||||
{viewerData?.me?.features.map((feature) => {
|
||||
return (
|
||||
<StyledText
|
||||
key={`feature-${feature}`}
|
||||
style="footnote"
|
||||
css={{ display: 'flex', gap: '5px' }}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
></input>
|
||||
{feature}
|
||||
</StyledText>
|
||||
)
|
||||
})}
|
||||
<StyledText
|
||||
style="footnote"
|
||||
css={{ display: 'flex', gap: '5px' }}
|
||||
>
|
||||
To learn more about beta features available,{' '}
|
||||
<a href="https://discord.gg/h2z5rppzz9">
|
||||
join the Omnivore Discord
|
||||
</a>
|
||||
</StyledText>
|
||||
</>
|
||||
)}
|
||||
{/* <Button style="ctaDarkYellow">Upgrade</Button> */}
|
||||
</VStack>
|
||||
|
||||
<VStack
|
||||
css={{
|
||||
padding: '24px',
|
||||
|
||||
@ -439,3 +439,28 @@ export class GroupMembership extends BaseEntity {
|
||||
@Column('boolean', { default: false })
|
||||
is_admin!: boolean
|
||||
}
|
||||
|
||||
@Entity({ name: 'features' })
|
||||
export class Feature extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
@ManyToOne(() => User, (user) => user.articles, { eager: true })
|
||||
user!: User
|
||||
|
||||
@Column('text')
|
||||
name!: string
|
||||
|
||||
@Column('timestamp', { nullable: true })
|
||||
grantedAt?: Date | null
|
||||
|
||||
@Column('timestamp', { nullable: true })
|
||||
expiresAt?: Date | null
|
||||
|
||||
@Column({ type: 'timestamp', name: 'created_at' })
|
||||
createdAt!: Date
|
||||
|
||||
@Column({ type: 'timestamp', name: 'updated_at' })
|
||||
updatedAt!: Date
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
LibraryItem,
|
||||
Recommendation,
|
||||
GroupMembership,
|
||||
Feature,
|
||||
} from './db'
|
||||
import { compare, hashSync } from 'bcryptjs'
|
||||
const readYamlFile = require('read-yaml-file')
|
||||
@ -50,6 +51,7 @@ const ADMIN_USER_EMAIL =
|
||||
},
|
||||
{ resource: Recommendation, options: { parent: { name: 'Users' } } },
|
||||
{ resource: GroupMembership, options: { parent: { name: 'Users' } } },
|
||||
{ resource: Feature, options: { parent: { name: 'Users' } } },
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
397
yarn.lock
397
yarn.lock
@ -50,6 +50,21 @@
|
||||
lodash "^4.17.21"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
"@anthropic-ai/sdk@^0.9.1":
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.9.1.tgz#b2d2b7bf05c90dce502c9a2e869066870f69ba88"
|
||||
integrity sha512-wa1meQ2WSfoY8Uor3EdrJq0jTiZJoKoSii2ZVWRY1oN4Tlr5s59pADg9T79FTbPe1/se5c3pBeZgJL63wmuoBA==
|
||||
dependencies:
|
||||
"@types/node" "^18.11.18"
|
||||
"@types/node-fetch" "^2.6.4"
|
||||
abort-controller "^3.0.0"
|
||||
agentkeepalive "^4.2.1"
|
||||
digest-fetch "^1.3.0"
|
||||
form-data-encoder "1.7.2"
|
||||
formdata-node "^4.3.2"
|
||||
node-fetch "^2.6.7"
|
||||
web-streams-polyfill "^3.2.1"
|
||||
|
||||
"@apollo/protobufjs@1.2.6":
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.6.tgz#d601e65211e06ae1432bf5993a1a0105f2862f27"
|
||||
@ -3601,6 +3616,57 @@
|
||||
dependencies:
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@langchain/community@~0.0.33":
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.0.33.tgz#5568fe36b1e2f8947d49414d47e14a27da5b65c9"
|
||||
integrity sha512-m7KfOB2t/6ZQkx29FcqHeOe+jxQZDJdRqpMsCAxRebCaIUiAwNJgRqqukQOcDsG574jhEyEMYuYDfImfXSY7aw==
|
||||
dependencies:
|
||||
"@langchain/core" "~0.1.36"
|
||||
"@langchain/openai" "~0.0.14"
|
||||
flat "^5.0.2"
|
||||
langsmith "~0.1.1"
|
||||
uuid "^9.0.0"
|
||||
zod "^3.22.3"
|
||||
|
||||
"@langchain/core@~0.1.13", "@langchain/core@~0.1.36", "@langchain/core@~0.1.39":
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.1.39.tgz#c9b993f857d935afe1b66d1cc001805f1b7db8a0"
|
||||
integrity sha512-bhsMOSLPkWbVZuE3uPd9dgiOeqFwexU7IGfjWht+mWrAW9spuBtAOKOcrmxK3v5TYaKpoDbIiH641FobyD947g==
|
||||
dependencies:
|
||||
ansi-styles "^5.0.0"
|
||||
camelcase "6"
|
||||
decamelize "1.2.0"
|
||||
js-tiktoken "^1.0.8"
|
||||
langsmith "~0.1.7"
|
||||
ml-distance "^4.0.0"
|
||||
p-queue "^6.6.2"
|
||||
p-retry "4"
|
||||
uuid "^9.0.0"
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.3"
|
||||
|
||||
"@langchain/openai@^0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.0.14.tgz#27a6ba83f6b754391868b22f3b90cd440038acf0"
|
||||
integrity sha512-co6nRylPrLGY/C3JYxhHt6cxLq07P086O7K3QaZH7SFFErIN9wSzJonpvhZR07DEUq6eK6wKgh2ORxA/NcjSRQ==
|
||||
dependencies:
|
||||
"@langchain/core" "~0.1.13"
|
||||
js-tiktoken "^1.0.7"
|
||||
openai "^4.26.0"
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.3"
|
||||
|
||||
"@langchain/openai@~0.0.14":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.0.15.tgz#541271bb36066422957ac7f822ca2a45281630b1"
|
||||
integrity sha512-ILecml9YopmQxfpaquYEG+KfEz7svJqpcla671J1tVngqtPwRqg9PLUOa2eDrPsunScxKUeLd8HjAGLd/eaefQ==
|
||||
dependencies:
|
||||
"@langchain/core" "~0.1.39"
|
||||
js-tiktoken "^1.0.7"
|
||||
openai "^4.26.0"
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.3"
|
||||
|
||||
"@lerna/child-process@7.4.1":
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.1.tgz#efacbbe79794ef977feb86873d853bb8708707be"
|
||||
@ -6012,6 +6078,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20"
|
||||
integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==
|
||||
|
||||
"@sqltools/formatter@^1.2.5":
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12"
|
||||
integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==
|
||||
|
||||
"@stitches/react@^1.2.5":
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@stitches/react/-/react-1.2.8.tgz#954f8008be8d9c65c4e58efa0937f32388ce3a38"
|
||||
@ -7812,6 +7883,14 @@
|
||||
"@types/node" "*"
|
||||
form-data "^3.0.0"
|
||||
|
||||
"@types/node-fetch@^2.6.4":
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24"
|
||||
integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
form-data "^4.0.0"
|
||||
|
||||
"@types/node-fetch@^2.6.6":
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.7.tgz#a1abe2ce24228b58ad97f99480fdcf9bbc6ab16d"
|
||||
@ -7840,6 +7919,20 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.68.tgz#3155f64a961b3d8d10246c80657f9a7292e3421a"
|
||||
integrity sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==
|
||||
|
||||
"@types/node@^18.11.18":
|
||||
version "18.19.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.19.tgz#fe8bdbf85683a461ad685ead52a57ab2ee2315e4"
|
||||
integrity sha512-qqV6hSy9zACEhQUy5CEGeuXAZN0fNjqLWRIvOXOwdFYhFoKBiY08VKR5kgchr90+TitLVhpUEb54hk4bYaArUw==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/node@^20.11.0":
|
||||
version "20.11.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.21.tgz#ad67e65652f7be15686e2df87a38076a81c5e9c5"
|
||||
integrity sha512-/ySDLGscFPNasfqStUuWWPfL78jompfIoVzLJPVVAHBh6rpG68+pI2Gk+fNLeI8/f1yPYL4s46EleVIc20F1Ow==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/nodemailer@^6.4.4":
|
||||
version "6.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.4.tgz#c265f7e7a51df587597b3a49a023acaf0c741f4b"
|
||||
@ -8006,6 +8099,11 @@
|
||||
"@types/tough-cookie" "*"
|
||||
form-data "^2.5.0"
|
||||
|
||||
"@types/retry@0.12.0":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||
|
||||
"@types/retry@^0.12.0":
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065"
|
||||
@ -8231,6 +8329,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.0.tgz#53ef263e5239728b56096b0a869595135b7952d2"
|
||||
integrity sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==
|
||||
|
||||
"@types/uuid@^9.0.1":
|
||||
version "9.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
|
||||
integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==
|
||||
|
||||
"@types/voca@^1.4.0":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/voca/-/voca-1.4.2.tgz#4d2ceef3582c2a1f6a1574f71f0897400d910583"
|
||||
@ -9357,6 +9460,11 @@ app-root-path@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
|
||||
integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==
|
||||
|
||||
app-root-path@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.1.0.tgz#5971a2fc12ba170369a7a1ef018c71e6e47c2e86"
|
||||
integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==
|
||||
|
||||
apparatus@^0.0.10:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/apparatus/-/apparatus-0.0.10.tgz#81ea756772ada77863db54ceee8202c109bdca3e"
|
||||
@ -10255,7 +10363,12 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1:
|
||||
base-64@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
|
||||
integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
@ -10381,6 +10494,11 @@ binary-extensions@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
binary-search@^1.3.5:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c"
|
||||
integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==
|
||||
|
||||
binary@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79"
|
||||
@ -11011,6 +11129,11 @@ camelcase-keys@^7.0.0:
|
||||
quick-lru "^5.1.1"
|
||||
type-fest "^1.2.1"
|
||||
|
||||
camelcase@6, camelcase@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
@ -11021,11 +11144,6 @@ camelcase@^6.0.0, camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
camelcase@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001251, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001317:
|
||||
version "1.0.30001527"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz"
|
||||
@ -11288,6 +11406,11 @@ chardet@^0.7.0:
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
charenc@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
|
||||
|
||||
check-error@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
@ -11830,6 +11953,11 @@ command-score@^0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/command-score/-/command-score-0.1.2.tgz#b986ad7e8c0beba17552a56636c44ae38363d381"
|
||||
integrity sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==
|
||||
|
||||
commander@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
|
||||
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
|
||||
|
||||
commander@^2.19.0, commander@^2.20.0, commander@^2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
@ -12481,6 +12609,11 @@ cross-undici-fetch@^0.1.19:
|
||||
undici "^4.9.3"
|
||||
web-streams-polyfill "^3.2.0"
|
||||
|
||||
crypt@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||
integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
|
||||
|
||||
crypto-browserify@^3.11.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||
@ -12790,7 +12923,7 @@ dateformat@^3.0.0, dateformat@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dayjs@1.x, dayjs@^1.10.4, dayjs@^1.11.7:
|
||||
dayjs@1.x, dayjs@^1.10.4, dayjs@^1.11.7, dayjs@^1.11.9:
|
||||
version "1.11.10"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
||||
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||
@ -12841,7 +12974,7 @@ decamelize-keys@^1.1.0:
|
||||
decamelize "^1.1.0"
|
||||
map-obj "^1.0.0"
|
||||
|
||||
decamelize@^1.1.0, decamelize@^1.2.0:
|
||||
decamelize@1.2.0, decamelize@^1.1.0, decamelize@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
@ -13260,6 +13393,14 @@ diffie-hellman@^5.0.0:
|
||||
miller-rabin "^4.0.0"
|
||||
randombytes "^2.0.0"
|
||||
|
||||
digest-fetch@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/digest-fetch/-/digest-fetch-1.3.0.tgz#898e69264d00012a23cf26e8a3e40320143fc661"
|
||||
integrity sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==
|
||||
dependencies:
|
||||
base-64 "^0.1.0"
|
||||
md5 "^2.3.0"
|
||||
|
||||
dir-glob@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
|
||||
@ -13513,6 +13654,11 @@ dotenv@^16.0.1:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d"
|
||||
integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==
|
||||
|
||||
dotenv@^16.0.3:
|
||||
version "16.4.5"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
|
||||
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
|
||||
|
||||
dotenv@^8.0.0, dotenv@^8.2.0:
|
||||
version "8.6.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
|
||||
@ -14783,6 +14929,11 @@ exponential-backoff@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
||||
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
|
||||
|
||||
expr-eval@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/expr-eval/-/expr-eval-2.0.2.tgz#fa6f044a7b0c93fde830954eb9c5b0f7fbc7e201"
|
||||
integrity sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==
|
||||
|
||||
express-http-context2@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/express-http-context2/-/express-http-context2-1.0.0.tgz#58cd9fb0d233739e0dcd7aabb766d1dc74522d77"
|
||||
@ -15491,6 +15642,11 @@ fork-ts-checker-webpack-plugin@^6.0.4:
|
||||
semver "^7.3.2"
|
||||
tapable "^1.0.0"
|
||||
|
||||
form-data-encoder@1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
|
||||
integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==
|
||||
|
||||
form-data-encoder@^1.7.1:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96"
|
||||
@ -15545,6 +15701,14 @@ formdata-node@^4.3.1:
|
||||
node-domexception "1.0.0"
|
||||
web-streams-polyfill "4.0.0-beta.1"
|
||||
|
||||
formdata-node@^4.3.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2"
|
||||
integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==
|
||||
dependencies:
|
||||
node-domexception "1.0.0"
|
||||
web-streams-polyfill "4.0.0-beta.3"
|
||||
|
||||
formidable@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff"
|
||||
@ -16159,7 +16323,7 @@ glob@7.2.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glo
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^10.2.2:
|
||||
glob@^10.2.2, glob@^10.3.10:
|
||||
version "10.3.10"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
|
||||
integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==
|
||||
@ -17759,6 +17923,11 @@ is-alphanumerical@^1.0.0:
|
||||
is-alphabetical "^1.0.0"
|
||||
is-decimal "^1.0.0"
|
||||
|
||||
is-any-array@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-2.0.1.tgz#9233242a9c098220290aa2ec28f82ca7fa79899e"
|
||||
integrity sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==
|
||||
|
||||
is-arguments@^1.0.4, is-arguments@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
||||
@ -17822,7 +17991,7 @@ is-boolean-object@^1.1.0:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-buffer@^1.0.2, is-buffer@^1.1.5:
|
||||
is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||
@ -19150,6 +19319,13 @@ js-string-escape@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
|
||||
integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=
|
||||
|
||||
js-tiktoken@^1.0.7, js-tiktoken@^1.0.8:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.10.tgz#2b343ec169399dcee8f9ef9807dbd4fafd3b30dc"
|
||||
integrity sha512-ZoSxbGjvGyMT13x6ACo9ebhDha/0FHdKA+OsQcMOWcm1Zs7r90Rhk5lhERLzji+3rA7EKpXCgwXcM5fF3DMpdA==
|
||||
dependencies:
|
||||
base64-js "^1.5.1"
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -19401,6 +19577,11 @@ jsonparse@^1.2.0, jsonparse@^1.3.1:
|
||||
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
|
||||
integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
|
||||
|
||||
jsonpointer@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559"
|
||||
integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==
|
||||
|
||||
jsonwebtoken@^8.5.1:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
@ -19648,6 +19829,46 @@ kuler@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
|
||||
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
|
||||
|
||||
langchain@^0.1.21:
|
||||
version "0.1.23"
|
||||
resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.1.23.tgz#51eb49548c5514aefa2fca68509f424ead58ae04"
|
||||
integrity sha512-IzvF0c+mi+6kqrC0akWOQnJ0ynwkuh4qhg5bpgr56mlQItaJimO87ujktgxrOb1xMVU7fF9Y52SNc2Kjg7ihJw==
|
||||
dependencies:
|
||||
"@anthropic-ai/sdk" "^0.9.1"
|
||||
"@langchain/community" "~0.0.33"
|
||||
"@langchain/core" "~0.1.36"
|
||||
"@langchain/openai" "~0.0.14"
|
||||
binary-extensions "^2.2.0"
|
||||
expr-eval "^2.0.2"
|
||||
js-tiktoken "^1.0.7"
|
||||
js-yaml "^4.1.0"
|
||||
jsonpointer "^5.0.1"
|
||||
langchainhub "~0.0.8"
|
||||
langsmith "~0.1.7"
|
||||
ml-distance "^4.0.0"
|
||||
openapi-types "^12.1.3"
|
||||
p-retry "4"
|
||||
uuid "^9.0.0"
|
||||
yaml "^2.2.1"
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.3"
|
||||
|
||||
langchainhub@~0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/langchainhub/-/langchainhub-0.0.8.tgz#fd4b96dc795e22e36c1a20bad31b61b0c33d3110"
|
||||
integrity sha512-Woyb8YDHgqqTOZvWIbm2CaFDGfZ4NTSyXV687AG4vXEfoNo7cGQp7nhl7wL3ehenKWmNEmcxCLgOZzW8jE6lOQ==
|
||||
|
||||
langsmith@~0.1.1, langsmith@~0.1.7:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.1.8.tgz#a98c7c3d0ecbf6c44be4b30f3be66070c099b963"
|
||||
integrity sha512-GMEPhUPmkOPUih2ho07kSMhHYpCDkavc6Zg0XgBjhLsYqYaobOxFFNyOc806jOvH2yw2tmiKLuiAdlQAVbDnHg==
|
||||
dependencies:
|
||||
"@types/uuid" "^9.0.1"
|
||||
commander "^10.0.1"
|
||||
p-queue "^6.6.2"
|
||||
p-retry "4"
|
||||
uuid "^9.0.0"
|
||||
|
||||
language-subtag-registry@~0.3.2:
|
||||
version "0.3.21"
|
||||
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
|
||||
@ -20837,6 +21058,15 @@ md5.js@^1.3.4:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
md5@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
|
||||
integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
|
||||
dependencies:
|
||||
charenc "0.0.2"
|
||||
crypt "0.0.2"
|
||||
is-buffer "~1.1.6"
|
||||
|
||||
mdast-squeeze-paragraphs@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97"
|
||||
@ -21829,11 +22059,52 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
mkdirp@^2.1.3:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19"
|
||||
integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
|
||||
|
||||
mkdirp@~0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7"
|
||||
integrity sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==
|
||||
|
||||
ml-array-mean@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/ml-array-mean/-/ml-array-mean-1.1.6.tgz#d951a700dc8e3a17b3e0a583c2c64abd0c619c56"
|
||||
integrity sha512-MIdf7Zc8HznwIisyiJGRH9tRigg3Yf4FldW8DxKxpCCv/g5CafTw0RRu51nojVEOXuCQC7DRVVu5c7XXO/5joQ==
|
||||
dependencies:
|
||||
ml-array-sum "^1.1.6"
|
||||
|
||||
ml-array-sum@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/ml-array-sum/-/ml-array-sum-1.1.6.tgz#d1d89c20793cd29c37b09d40e85681aa4515a955"
|
||||
integrity sha512-29mAh2GwH7ZmiRnup4UyibQZB9+ZLyMShvt4cH4eTK+cL2oEMIZFnSyB3SS8MlsTh6q/w/yh48KmqLxmovN4Dw==
|
||||
dependencies:
|
||||
is-any-array "^2.0.0"
|
||||
|
||||
ml-distance-euclidean@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ml-distance-euclidean/-/ml-distance-euclidean-2.0.0.tgz#3a668d236649d1b8fec96380b9435c6f42c9a817"
|
||||
integrity sha512-yC9/2o8QF0A3m/0IXqCTXCzz2pNEzvmcE/9HFKOZGnTjatvBbsn4lWYJkxENkA4Ug2fnYl7PXQxnPi21sgMy/Q==
|
||||
|
||||
ml-distance@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ml-distance/-/ml-distance-4.0.1.tgz#4741d17a1735888c5388823762271dfe604bd019"
|
||||
integrity sha512-feZ5ziXs01zhyFUUUeZV5hwc0f5JW0Sh0ckU1koZe/wdVkJdGxcP06KNQuF0WBTj8FttQUzcvQcpcrOp/XrlEw==
|
||||
dependencies:
|
||||
ml-array-mean "^1.1.6"
|
||||
ml-distance-euclidean "^2.0.0"
|
||||
ml-tree-similarity "^1.0.0"
|
||||
|
||||
ml-tree-similarity@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ml-tree-similarity/-/ml-tree-similarity-1.0.0.tgz#24705a107e32829e24d945e87219e892159c53f0"
|
||||
integrity sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg==
|
||||
dependencies:
|
||||
binary-search "^1.3.5"
|
||||
num-sort "^2.0.0"
|
||||
|
||||
mocha-unfunk-reporter@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/mocha-unfunk-reporter/-/mocha-unfunk-reporter-0.4.0.tgz#59eda97aec6ae6e26d7af4173490a65b7b082d20"
|
||||
@ -22947,6 +23218,11 @@ nullthrows@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
|
||||
integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
|
||||
|
||||
num-sort@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/num-sort/-/num-sort-2.1.0.tgz#1cbb37aed071329fdf41151258bc011898577a9b"
|
||||
integrity sha512-1MQz1Ed8z2yckoBeSfkQHHO9K1yDRxxtotKSJ9yvcTUUxSvfvzEq5GwBrjjHEpMlq/k5gvXdmJ1SbYxWtpNoVg==
|
||||
|
||||
num2fraction@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
|
||||
@ -23365,6 +23641,26 @@ open@^8.4.0:
|
||||
is-docker "^2.1.1"
|
||||
is-wsl "^2.2.0"
|
||||
|
||||
openai@^4.26.0:
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/openai/-/openai-4.28.0.tgz#ded00e3d98c25758b5406c9675ec27a957e00930"
|
||||
integrity sha512-JM8fhcpmpGN0vrUwGquYIzdcEQHtFuom6sRCbbCM6CfzZXNuRk33G7KfeRAIfnaCxSpzrP5iHtwJzIm6biUZ2Q==
|
||||
dependencies:
|
||||
"@types/node" "^18.11.18"
|
||||
"@types/node-fetch" "^2.6.4"
|
||||
abort-controller "^3.0.0"
|
||||
agentkeepalive "^4.2.1"
|
||||
digest-fetch "^1.3.0"
|
||||
form-data-encoder "1.7.2"
|
||||
formdata-node "^4.3.2"
|
||||
node-fetch "^2.6.7"
|
||||
web-streams-polyfill "^3.2.1"
|
||||
|
||||
openapi-types@^12.1.3:
|
||||
version "12.1.3"
|
||||
resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3"
|
||||
integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==
|
||||
|
||||
opener@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
|
||||
@ -23603,7 +23899,7 @@ p-pipe@3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e"
|
||||
integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==
|
||||
|
||||
p-queue@6.6.2:
|
||||
p-queue@6.6.2, p-queue@^6.6.2:
|
||||
version "6.6.2"
|
||||
resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
|
||||
integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
|
||||
@ -23621,6 +23917,14 @@ p-reduce@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-3.0.0.tgz#f11773794792974bd1f7a14c72934248abff4160"
|
||||
integrity sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==
|
||||
|
||||
p-retry@4:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
|
||||
integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
|
||||
dependencies:
|
||||
"@types/retry" "0.12.0"
|
||||
retry "^0.13.1"
|
||||
|
||||
p-retry@^4.5.0:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c"
|
||||
@ -26047,6 +26351,14 @@ read-pkg@^7.1.0:
|
||||
parse-json "^5.2.0"
|
||||
type-fest "^2.0.0"
|
||||
|
||||
read-yaml-file@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/read-yaml-file/-/read-yaml-file-2.1.0.tgz#c5866712db9ef5343b4d02c2413bada53c41c4a9"
|
||||
integrity sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==
|
||||
dependencies:
|
||||
js-yaml "^4.0.0"
|
||||
strip-bom "^4.0.0"
|
||||
|
||||
read@1, read@^1.0.7, read@~1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
|
||||
@ -26189,6 +26501,11 @@ reflect-metadata@^0.1.13:
|
||||
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
|
||||
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
||||
|
||||
reflect-metadata@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74"
|
||||
integrity sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==
|
||||
|
||||
reflect.getprototypeof@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3"
|
||||
@ -29214,7 +29531,7 @@ tslib@^1.0.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0:
|
||||
tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
@ -29430,6 +29747,27 @@ typeorm-naming-strategies@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/typeorm-naming-strategies/-/typeorm-naming-strategies-4.1.0.tgz#1ec6eb296c8d7b69bb06764d5b9083ff80e814a9"
|
||||
integrity sha512-vPekJXzZOTZrdDvTl1YoM+w+sUIfQHG4kZTpbFYoTsufyv9NIBRe4Q+PdzhEAFA2std3D9LZHEb1EjE9zhRpiQ==
|
||||
|
||||
typeorm@^0.3.19:
|
||||
version "0.3.20"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.20.tgz#4b61d737c6fed4e9f63006f88d58a5e54816b7ab"
|
||||
integrity sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==
|
||||
dependencies:
|
||||
"@sqltools/formatter" "^1.2.5"
|
||||
app-root-path "^3.1.0"
|
||||
buffer "^6.0.3"
|
||||
chalk "^4.1.2"
|
||||
cli-highlight "^2.1.11"
|
||||
dayjs "^1.11.9"
|
||||
debug "^4.3.4"
|
||||
dotenv "^16.0.3"
|
||||
glob "^10.3.10"
|
||||
mkdirp "^2.1.3"
|
||||
reflect-metadata "^0.2.1"
|
||||
sha.js "^2.4.11"
|
||||
tslib "^2.5.0"
|
||||
uuid "^9.0.0"
|
||||
yargs "^17.6.2"
|
||||
|
||||
typeorm@^0.3.4:
|
||||
version "0.3.7"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.7.tgz#5776ed5058f0acb75d64723b39ff458d21de64c1"
|
||||
@ -29468,6 +29806,11 @@ typescript@^4.4.4:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
|
||||
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
|
||||
|
||||
typescript@^5.3.3:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
|
||||
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
|
||||
|
||||
ua-parser-js@^0.7.30:
|
||||
version "0.7.33"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532"
|
||||
@ -29546,6 +29889,11 @@ underscore@^1.13.4, underscore@^1.9.1:
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee"
|
||||
integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
undici@^4.9.3:
|
||||
version "4.14.1"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-4.14.1.tgz#7633b143a8a10d6d63335e00511d071e8d52a1d9"
|
||||
@ -30309,11 +30657,21 @@ web-streams-polyfill@4.0.0-beta.1:
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95"
|
||||
integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ==
|
||||
|
||||
web-streams-polyfill@4.0.0-beta.3:
|
||||
version "4.0.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38"
|
||||
integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==
|
||||
|
||||
web-streams-polyfill@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965"
|
||||
integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==
|
||||
|
||||
web-streams-polyfill@^3.2.1:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
|
||||
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
@ -31061,6 +31419,11 @@ yaml@^1.7.2:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yaml@^2.2.1:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140"
|
||||
integrity sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==
|
||||
|
||||
yargs-parser@20.2.4:
|
||||
version "20.2.4"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
|
||||
@ -31207,6 +31570,16 @@ yup@^0.31.0:
|
||||
property-expr "^2.0.4"
|
||||
toposort "^2.0.2"
|
||||
|
||||
zod-to-json-schema@^3.22.3:
|
||||
version "3.22.4"
|
||||
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.4.tgz#f8cc691f6043e9084375e85fb1f76ebafe253d70"
|
||||
integrity sha512-2Ed5dJ+n/O3cU383xSY28cuVi0BCQhF8nYqWU5paEpl7fVdqdAmiLdqLyfblbNdfOFwFfi/mqU4O1pwc60iBhQ==
|
||||
|
||||
zod@^3.22.3, zod@^3.22.4:
|
||||
version "3.22.4"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
|
||||
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==
|
||||
|
||||
zwitch@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
||||
|
||||
Reference in New Issue
Block a user