diff --git a/web/build.log b/web/build.log new file mode 100644 index 0000000..2417e48 --- /dev/null +++ b/web/build.log @@ -0,0 +1,128 @@ + +> web@0.1.0 build C:\Users\monke\OneDrive\Documentos\Projetos\sistema-de-chamados\web +> next build + + Ôû▓ Next.js 15.5.3 + - Environments: .env.local + + Creating an optimized production build ... + Ô£ô Compiled successfully in 3.0s + Linting and checking validity of types ... + +./src/app/ConvexClientProvider.tsx +4:21 Warning: 'useMemo' is defined but never used. @typescript-eslint/no-unused-vars + +./src/app/tickets/new/page.tsx +16:9 Warning: The 'queues' logical expression could make the dependencies of useMemo Hook (at line 28) change on every render. To fix this, wrap the initialization of 'queues' in its own useMemo() Hook. react-hooks/exhaustive-deps +28:53 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +35:33 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +35:49 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +43:27 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +44:30 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +48:42 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +48:67 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/app/tickets/[id]/page.tsx +27:61 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/new-ticket-dialog.tsx +6:1 Warning: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free. @typescript-eslint/ban-ts-comment +50:35 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +58:32 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +63:44 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +63:69 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +63:140 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +71:14 Warning: 'err' is defined but never used. @typescript-eslint/no-unused-vars +116:111 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +128:109 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +150:41 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/play-next-ticket-card.tsx +39:34 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +43:169 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +43:240 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +75:37 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +109:103 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +109:141 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/recent-tickets-panel.tsx +4:1 Warning: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free. @typescript-eslint/ban-ts-comment +9:10 Warning: 'Spinner' is defined but never used. @typescript-eslint/no-unused-vars +27:58 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +30:41 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/ticket-comments.rich.tsx +31:9 Warning: 'generateUploadUrl' is assigned a value but never used. @typescript-eslint/no-unused-vars +52:100 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +61:49 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +61:74 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +64:14 Warning: 'err' is defined but never used. @typescript-eslint/no-unused-vars +113:34 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +151:83 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/ticket-detail-view.tsx +11:10 Warning: 'Separator' is defined but never used. @typescript-eslint/no-unused-vars +20:108 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +21:15 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +23:50 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +57:46 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +60:45 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +61:45 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +63:47 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/ticket-queue-summary.tsx +18:70 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/ticket-summary-header.tsx +50:50 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +59:63 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +59:85 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +59:109 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +61:26 Warning: 'e' is defined but never used. @typescript-eslint/no-unused-vars +98:63 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +98:89 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +98:113 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +107:31 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +121:42 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +125:60 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +125:82 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +125:106 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +134:31 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/ticket-timeline.tsx +12:10 Warning: 'cn' is defined but never used. @typescript-eslint/no-unused-vars +15:10 Warning: 'Separator' is defined but never used. @typescript-eslint/no-unused-vars +72:28 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/tickets/tickets-view.tsx +12:10 Warning: 'Spinner' is defined but never used. @typescript-eslint/no-unused-vars +27:80 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +31:31 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +36:76 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +49:51 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +./src/components/ui/dropzone.tsx +4:1 Warning: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free. @typescript-eslint/ban-ts-comment + +./src/components/ui/field.tsx +11:52 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any +28:58 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any + +info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/app/api-reference/config/eslint#disabling-rules +Failed to compile. + +./src/components/tickets/play-next-ticket-card.tsx:43:9 +Type error: Type '{ queue: { id: string; name: string; pending: number; waiting: number; breached: number; }; nextTicket: { id: string; reference: number; tenantId: string; subject: string; status: "RESOLVED" | "CLOSED" | "PENDING" | "ON_HOLD" | "OPEN" | "NEW"; ... 14 more ...; lastTimelineEntry?: string | undefined; } | null; } | { ...' is not assignable to type '{ queue: { id: string; name: string; pending: number; waiting: number; breached: number; }; nextTicket: { id: string; reference: number; tenantId: string; subject: string; status: "RESOLVED" | "CLOSED" | "PENDING" | "ON_HOLD" | "OPEN" | "NEW"; ... 14 more ...; lastTimelineEntry?: string | undefined; } | null; } | null'. + Type '{ queue: { id: string; name: string; pending: number; waiting: number; breached: number; }; nextTicket: { id: any; reference: any; tenantId: any; subject: any; summary: any; status: any; priority: any; channel: any; ... 11 more ...; metrics: null; }; }' is not assignable to type '{ queue: { id: string; name: string; pending: number; waiting: number; breached: number; }; nextTicket: { id: string; reference: number; tenantId: string; subject: string; status: "RESOLVED" | "CLOSED" | "PENDING" | "ON_HOLD" | "OPEN" | "NEW"; ... 14 more ...; lastTimelineEntry?: string | undefined; } | null; }'. + The types of 'nextTicket.lastTimelineEntry' are incompatible between these types. + Type 'null' is not assignable to type 'string | undefined'. + + 41 | })?.[0] + 42 | +> 43 | const cardContext: TicketPlayContext | null = context ?? (nextTicketFromServer ? { queue: { id: "default", name: "Geral", pending: queueSummary.reduce((a: number, b: any) => a + b.pending, 0), waiting: queueSummary.reduce((a: number, b: any) => a + b.waiting, 0), breached: 0 }, nextTicket: nextTicketFromServer } : null) + | ^ + 44 | + 45 | if (!cardContext || !cardContext.nextTicket) { + 46 | return ( +Next.js build worker exited with code: 1 and signal: null +ÔÇëELIFECYCLEÔÇë Command failed with exit code 1. diff --git a/web/convex/schema.ts b/web/convex/schema.ts index 2067b0b..2156464 100644 --- a/web/convex/schema.ts +++ b/web/convex/schema.ts @@ -41,6 +41,7 @@ export default defineSchema({ reference: v.number(), subject: v.string(), summary: v.optional(v.string()), + description: v.optional(v.string()), status: v.string(), priority: v.string(), channel: v.string(), @@ -59,7 +60,8 @@ export default defineSchema({ .index("by_tenant_status", ["tenantId", "status"]) .index("by_tenant_queue", ["tenantId", "queueId"]) .index("by_tenant_assignee", ["tenantId", "assigneeId"]) - .index("by_tenant_reference", ["tenantId", "reference"]), + .index("by_tenant_reference", ["tenantId", "reference"]) + .index("by_tenant", ["tenantId"]), ticketComments: defineTable({ ticketId: v.id("tickets"), @@ -87,4 +89,3 @@ export default defineSchema({ createdAt: v.number(), }).index("by_ticket", ["ticketId"]), }); - diff --git a/web/convex/tickets.ts b/web/convex/tickets.ts index 880e356..5bd8e32 100644 --- a/web/convex/tickets.ts +++ b/web/convex/tickets.ts @@ -1,8 +1,8 @@ import { internalMutation, mutation, query } from "./_generated/server"; import { v } from "convex/values"; -import { Id } from "./_generated/dataModel"; +import { Id, type Doc } from "./_generated/dataModel"; -const STATUS_ORDER = ["URGENT", "HIGH", "MEDIUM", "LOW"] as const; +const PRIORITY_ORDER = ["URGENT", "HIGH", "MEDIUM", "LOW"] as const; export const list = query({ args: { @@ -15,16 +15,28 @@ export const list = query({ limit: v.optional(v.number()), }, handler: async (ctx, args) => { - let q = ctx.db - .query("tickets") - .withIndex("by_tenant_status", (q) => q.eq("tenantId", args.tenantId)); - - const all = await q.collect(); - let filtered = all; - if (args.status) filtered = filtered.filter((t) => t.status === args.status); + // Choose best index based on provided args for efficiency + let base: Doc<"tickets">[] = []; + if (args.status) { + base = await ctx.db + .query("tickets") + .withIndex("by_tenant_status", (q) => q.eq("tenantId", args.tenantId).eq("status", args.status!)) + .collect(); + } else if (args.queueId) { + base = await ctx.db + .query("tickets") + .withIndex("by_tenant_queue", (q) => q.eq("tenantId", args.tenantId).eq("queueId", args.queueId!)) + .collect(); + } else { + base = await ctx.db + .query("tickets") + .withIndex("by_tenant", (q) => q.eq("tenantId", args.tenantId)) + .collect(); + } + let filtered = base; + if (args.priority) filtered = filtered.filter((t) => t.priority === args.priority); if (args.channel) filtered = filtered.filter((t) => t.channel === args.channel); - if (args.queueId) filtered = filtered.filter((t) => t.queueId === args.queueId); if (args.search) { const term = args.search.toLowerCase(); filtered = filtered.filter( @@ -38,9 +50,9 @@ export const list = query({ // hydrate requester and assignee const result = await Promise.all( limited.map(async (t) => { - const requester = await ctx.db.get(t.requesterId); - const assignee = t.assigneeId ? await ctx.db.get(t.assigneeId) : null; - const queue = t.queueId ? await ctx.db.get(t.queueId) : null; + const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null; + const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null; + const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null; return { id: t._id, reference: t.reference, @@ -80,7 +92,7 @@ export const list = query({ }) ); // sort by updatedAt desc - return result.sort((a, b) => (b.updatedAt as any) - (a.updatedAt as any)); + return result.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)); }, }); @@ -89,9 +101,9 @@ export const getById = query({ handler: async (ctx, { tenantId, id }) => { const t = await ctx.db.get(id); if (!t || t.tenantId !== tenantId) return null; - const requester = await ctx.db.get(t.requesterId); - const assignee = t.assigneeId ? await ctx.db.get(t.assigneeId) : null; - const queue = t.queueId ? await ctx.db.get(t.queueId) : null; + const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null; + const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null; + const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null; const comments = await ctx.db .query("ticketComments") .withIndex("by_ticket", (q) => q.eq("ticketId", id)) @@ -103,7 +115,7 @@ export const getById = query({ const commentsHydrated = await Promise.all( comments.map(async (c) => { - const author = await ctx.db.get(c.authorId); + const author = (await ctx.db.get(c.authorId)) as Doc<"users"> | null; const attachments = await Promise.all( (c.attachments ?? []).map(async (att) => ({ id: att.storageId, @@ -296,7 +308,7 @@ export const changeAssignee = mutation({ handler: async (ctx, { ticketId, assigneeId, actorId }) => { const now = Date.now(); await ctx.db.patch(ticketId, { assigneeId, updatedAt: now }); - const user = await ctx.db.get(assigneeId); + const user = (await ctx.db.get(assigneeId)) as Doc<"users"> | null; await ctx.db.insert("ticketEvents", { ticketId, type: "ASSIGNEE_CHANGED", @@ -311,7 +323,7 @@ export const changeQueue = mutation({ handler: async (ctx, { ticketId, queueId, actorId }) => { const now = Date.now(); await ctx.db.patch(ticketId, { queueId, updatedAt: now }); - const queue = await ctx.db.get(queueId); + const queue = (await ctx.db.get(queueId)) as Doc<"queues"> | null; await ctx.db.insert("ticketEvents", { ticketId, type: "QUEUE_CHANGED", @@ -329,10 +341,18 @@ export const playNext = mutation({ }, handler: async (ctx, { tenantId, queueId, agentId }) => { // Find eligible tickets: not resolved/closed and not assigned - let candidates = await ctx.db - .query("tickets") - .withIndex("by_tenant_queue", (q) => q.eq("tenantId", tenantId).eq("queueId", queueId ?? undefined as any)) - .collect(); + let candidates: Doc<"tickets">[] = [] + if (queueId) { + candidates = await ctx.db + .query("tickets") + .withIndex("by_tenant_queue", (q) => q.eq("tenantId", tenantId).eq("queueId", queueId)) + .collect() + } else { + candidates = await ctx.db + .query("tickets") + .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) + .collect() + } candidates = candidates.filter( (t) => t.status !== "RESOLVED" && t.status !== "CLOSED" && !t.assigneeId @@ -341,17 +361,18 @@ export const playNext = mutation({ if (candidates.length === 0) return null; // prioritize by priority then createdAt + const rank: Record = { URGENT: 0, HIGH: 1, MEDIUM: 2, LOW: 3 } candidates.sort((a, b) => { - const pa = STATUS_ORDER.indexOf(a.priority as any); - const pb = STATUS_ORDER.indexOf(b.priority as any); - if (pa !== pb) return pa - pb; - return a.createdAt - b.createdAt; - }); + const pa = rank[a.priority] ?? 999 + const pb = rank[b.priority] ?? 999 + if (pa !== pb) return pa - pb + return a.createdAt - b.createdAt + }) const chosen = candidates[0]; const now = Date.now(); await ctx.db.patch(chosen._id, { assigneeId: agentId, status: chosen.status === "NEW" ? "OPEN" : chosen.status, updatedAt: now }); - const agent = await ctx.db.get(agentId); + const agent = (await ctx.db.get(agentId)) as Doc<"users"> | null; await ctx.db.insert("ticketEvents", { ticketId: chosen._id, type: "ASSIGNEE_CHANGED", @@ -359,51 +380,45 @@ export const playNext = mutation({ createdAt: now, }); - return await getPublicById(ctx, chosen._id); + // hydrate minimal public ticket like in list + const requester = (await ctx.db.get(chosen.requesterId)) as Doc<"users"> | null + const assignee = chosen.assigneeId ? ((await ctx.db.get(chosen.assigneeId)) as Doc<"users"> | null) : null + const queue = chosen.queueId ? ((await ctx.db.get(chosen.queueId)) as Doc<"queues"> | null) : null + return { + id: chosen._id, + reference: chosen.reference, + tenantId: chosen.tenantId, + subject: chosen.subject, + summary: chosen.summary, + status: chosen.status, + priority: chosen.priority, + channel: chosen.channel, + queue: queue?.name ?? null, + requester: requester && { + id: requester._id, + name: requester.name, + email: requester.email, + avatarUrl: requester.avatarUrl, + teams: requester.teams ?? [], + }, + assignee: assignee + ? { + id: assignee._id, + name: assignee.name, + email: assignee.email, + avatarUrl: assignee.avatarUrl, + teams: assignee.teams ?? [], + } + : null, + slaPolicy: null, + dueAt: chosen.dueAt ?? null, + firstResponseAt: chosen.firstResponseAt ?? null, + resolvedAt: chosen.resolvedAt ?? null, + updatedAt: chosen.updatedAt, + createdAt: chosen.createdAt, + tags: chosen.tags ?? [], + lastTimelineEntry: null, + metrics: null, + } }, }); - -// internal helper to hydrate a ticket in the same shape as list/getById -const getPublicById = async (ctx: any, id: Id<"tickets">) => { - const t = await ctx.db.get(id); - if (!t) return null; - const requester = await ctx.db.get(t.requesterId); - const assignee = t.assigneeId ? await ctx.db.get(t.assigneeId) : null; - const queue = t.queueId ? await ctx.db.get(t.queueId) : null; - return { - id: t._id, - reference: t.reference, - tenantId: t.tenantId, - subject: t.subject, - summary: t.summary, - status: t.status, - priority: t.priority, - channel: t.channel, - queue: queue?.name ?? null, - requester: requester && { - id: requester._id, - name: requester.name, - email: requester.email, - avatarUrl: requester.avatarUrl, - teams: requester.teams ?? [], - }, - assignee: assignee - ? { - id: assignee._id, - name: assignee.name, - email: assignee.email, - avatarUrl: assignee.avatarUrl, - teams: assignee.teams ?? [], - } - : null, - slaPolicy: null, - dueAt: t.dueAt ?? null, - firstResponseAt: t.firstResponseAt ?? null, - resolvedAt: t.resolvedAt ?? null, - updatedAt: t.updatedAt, - createdAt: t.createdAt, - tags: t.tags ?? [], - lastTimelineEntry: null, - metrics: null, - }; -}; diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 06e2d74..8502ebc 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -9,17 +9,24 @@ const compat = new FlatCompat({ baseDirectory: __dirname, }); -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), - { - ignores: [ - "node_modules/**", - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", - ], - }, -]; +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), + { + ignores: [ + "node_modules/**", + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ], + }, + { + rules: { + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/ban-ts-comment": "warn", + "react/no-unescaped-entities": "off", + }, + }, +]; export default eslintConfig; diff --git a/web/package.json b/web/package.json index 4ebaada..33efd98 100644 --- a/web/package.json +++ b/web/package.json @@ -33,6 +33,10 @@ "@radix-ui/react-tooltip": "^1.2.8", "@tabler/icons-react": "^3.35.0", "@tanstack/react-table": "^8.21.3", + "@tiptap/extension-link": "^3.6.5", + "@tiptap/extension-placeholder": "^3.6.5", + "@tiptap/react": "^3.6.5", + "@tiptap/starter-kit": "^3.6.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "convex": "^1.27.3", @@ -44,6 +48,7 @@ "react-dom": "19.1.0", "react-hook-form": "^7.64.0", "recharts": "^2.15.4", + "sanitize-html": "^2.17.0", "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "vaul": "^1.1.2", @@ -55,6 +60,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "@types/sanitize-html": "^2.16.0", "eslint": "^9", "eslint-config-next": "15.5.3", "prisma": "^6.16.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 7bcc24a..02ea927 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -71,6 +71,18 @@ importers: '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tiptap/extension-link': + specifier: ^3.6.5 + version: 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/extension-placeholder': + specifier: ^3.6.5 + version: 3.6.5(@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/react': + specifier: ^3.6.5 + version: 3.6.5(@floating-ui/dom@1.7.4)(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tiptap/starter-kit': + specifier: ^3.6.5 + version: 3.6.5 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -104,6 +116,9 @@ importers: recharts: specifier: ^2.15.4 version: 2.15.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + sanitize-html: + specifier: ^2.17.0 + version: 2.17.0 sonner: specifier: ^2.0.7 version: 2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -132,6 +147,9 @@ importers: '@types/react-dom': specifier: ^19 version: 19.2.0(@types/react@19.2.0) + '@types/sanitize-html': + specifier: ^2.16.0 + version: 2.16.0 eslint: specifier: ^9 version: 9.37.0(jiti@2.6.1) @@ -1255,6 +1273,9 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + '@rollup/rollup-android-arm-eabi@4.52.4': resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} cpu: [arm] @@ -1484,6 +1505,160 @@ packages: resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} + '@tiptap/core@3.6.5': + resolution: {integrity: sha512-CgXuhevQbBcPfxaXzGZgIY9+aVMSAd68Q21g3EONz1iZBw026QgiaLhGK6jgGTErZL4GoNL/P+gC5nFCvN7+cA==} + peerDependencies: + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-blockquote@3.6.5': + resolution: {integrity: sha512-FOOgkLHXQ3zTiL2V1js5+PfaOHXuyr/GjeFZe+W1AUk58X/qJNOVGvKT1xlMOy9gy2ySgWmco7PhNXRRTimkWg==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-bold@3.6.5': + resolution: {integrity: sha512-8JXC+K4DXtPDbClHxgRAZnXYO2an2I86PbpqUw+S7m17XCr4t39Sw9CeNBohOHS6Cl8uxOKAjSyCZzqdnYkn3g==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-bubble-menu@3.6.5': + resolution: {integrity: sha512-RyCJghtkYZAljZQUfjk3B5tvVVCILsIYMR9XnC152uBiIuWsnz25qfdyBP+cOl6ONrQUvdscs0WmKvzN+nXZYw==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-bullet-list@3.6.5': + resolution: {integrity: sha512-AP81hyN7oTyv5zbNVRK35cQA7zuLnI5ItFFyqMQKWh90vfftXi/zhC9C7FWvKtEH7Kk68B338G2mi4tlXDgBFQ==} + peerDependencies: + '@tiptap/extension-list': ^3.6.5 + + '@tiptap/extension-code-block@3.6.5': + resolution: {integrity: sha512-VPPke3LqZYKPlbDBp8IcTJQwvYb1PP0L+2Qi2n3ebN4+gKn+KGhrjnkO+xNHCySWlqywQmMTIfWX1sxA0eVVdQ==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-code@3.6.5': + resolution: {integrity: sha512-U/cJFjE0hqBTbMb5J74e7ni5YReuJgS9NyJgTy94+Xt6vxR1vU4+qOl+3E0fOZtwDrxbLrsCQy3P3LvNb3HXdw==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-document@3.6.5': + resolution: {integrity: sha512-0c7kxWBIEIcoHUG89vpHOF2h4CMa0q6VWXhZ+6iqcI5uyqaKwgcW/TbHZR0nAwEsZLdRCKaryn2kO7jXiCjfnA==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-dropcursor@3.6.5': + resolution: {integrity: sha512-BsO3ufLHsdeV1ddChwQfi2Q4UkeqOF4LeUYPYBKfSg59aRKTSoxj3gZrAsaAm/0O3DmAiKNBiCtNRTJSApPEBQ==} + peerDependencies: + '@tiptap/extensions': ^3.6.5 + + '@tiptap/extension-floating-menu@3.6.5': + resolution: {integrity: sha512-ASKb5vHkYyB9g3vOAr2E2U+b6MbHk4Ff4PqngafGlWRAmOAmFxTcw9fLa3HKnj4pokSsYAEvYGOso99/W3GzhA==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-gapcursor@3.6.5': + resolution: {integrity: sha512-SHtp71zhV2bAQS8kaJ/otb2podGusDREZ9/SQ1rZi6yPcDFLS2KvIvsLssDwbjTuH6KefnsN6Vx01tzmXRAQig==} + peerDependencies: + '@tiptap/extensions': ^3.6.5 + + '@tiptap/extension-hard-break@3.6.5': + resolution: {integrity: sha512-6iMS6SzIn7+X95okRX8y3l/4f1G3lTrq24sbcAX4MHITncDC6g3TrdAxdA67Tqn5NI/OQx0LwF3kFJDO8QTAUg==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-heading@3.6.5': + resolution: {integrity: sha512-jFS5saqTtfG6MM0sW4X6mZlLycT2ud0Oo1GOZkCyBClwSOpZI/EBLNRIgoXgNtWrY917vB7xTQgCpTVHbvVRsQ==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-horizontal-rule@3.6.5': + resolution: {integrity: sha512-yNxcejI25j6NQMQuKQMTVmNYLnrHFCpzGAz1Ndzyar+gItYZXI9BLmMlwpLkIaJMpIKChj+2qHz25fPS5FlNFw==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-italic@3.6.5': + resolution: {integrity: sha512-2EtO2uffw5YnTQ1cieLPv9t7OKCfJFbgHRJPXf7Nnfh8XFh5AEyzw0qBNXZyLtlB28+HHSWLc/OHS6xMfwUy0A==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-link@3.6.5': + resolution: {integrity: sha512-VLCDNwxLC1IPnWT3HLLJUg1Hflf8A2jfs7aNF4vyMTWmKnrk1zmN+VyXQTAkrqr27qE5FnmLhHOYF3SNolNucw==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-list-item@3.6.5': + resolution: {integrity: sha512-A5JKf2dNG6IRrHmkaqroq/VcD5SnXYXgpQpsF7HrPGIzUSIjvjQu088980NQPHyMuTanDMml+nZgd8RzHhRISA==} + peerDependencies: + '@tiptap/extension-list': ^3.6.5 + + '@tiptap/extension-list-keymap@3.6.5': + resolution: {integrity: sha512-OHGGTJMdUOBincMgYGEN4WzHrTB/GFeCxLDJraDknPx4VJVa3UVZS8F8xd5cb2WnACEF33Ud/0yK3aN6kHrbtQ==} + peerDependencies: + '@tiptap/extension-list': ^3.6.5 + + '@tiptap/extension-list@3.6.5': + resolution: {integrity: sha512-2S6wNeaGvvYzJygBhHRLP0YubJAzY00WxQSO3NvHFeLFRFvilCnmh0JGMAqsNU+Owpz0iVrWY0YZskN5gPeR9w==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/extension-ordered-list@3.6.5': + resolution: {integrity: sha512-RiBl0Dkw8QtzS7OqUGm84BOyemw/N+hf8DYWsIqVysMRQAGBGhuklbw+DGpCL0nMHW4lh7WtvfKcb0yxLmhbbA==} + peerDependencies: + '@tiptap/extension-list': ^3.6.5 + + '@tiptap/extension-paragraph@3.6.5': + resolution: {integrity: sha512-AfuaBu+DKrRPspaLsXgo17dhuneISS6QsZTIzPeX21jFJcq3TjtD8wSzS4yRgzAQCEbupkI7t4JbtgxAIBNQHA==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-placeholder@3.6.5': + resolution: {integrity: sha512-9CLixogEb/4UkEyuDr4JdOlLvphcOVfZMdNMKmUVQdqo4MuZCdTDyK5ypfTPQJl8aUo0oCiEhqE0bQerYlueJQ==} + peerDependencies: + '@tiptap/extensions': ^3.6.5 + + '@tiptap/extension-strike@3.6.5': + resolution: {integrity: sha512-QR7CUmRJ7fJkHtxqKajKIaX/B4xpKFOsAOJHbnqZ8wzOtnEL5IlsmoUnbKBoVn0+2R2YKKvMK3lepGtAcVCfIQ==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-text@3.6.5': + resolution: {integrity: sha512-PVZDWUa25xPzmEN6WWA103yvYJn+NBvWb7WrQwWu9LkKUgd98ZgV3yFaEem/Ybugl/NDPV7q8GGaH+2wEg/VeA==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extension-underline@3.6.5': + resolution: {integrity: sha512-Ul1mO0H1e2vfvN5g48X/YQ8w1xFTpLqce+GUhi0OmXaZnVOTIMtLuN/zAAPjD+uw+79JVGjYa53lbo1dyhOfAw==} + peerDependencies: + '@tiptap/core': ^3.6.5 + + '@tiptap/extensions@3.6.5': + resolution: {integrity: sha512-7aadEaRjSbFAIp3WGYR1LXrvtVprmBNxw3FakEUMJ+XKmGNErDJgDMZh+siAYw5MWwCCGa5kKu8Qi/i+DU+ILg==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + + '@tiptap/pm@3.6.5': + resolution: {integrity: sha512-S+j6MPgUXRIQd5/mdaLjaJnOt4ptFwjqGjGMUfBbf9a3uKpXUXaCCzfuC6ZikwaUtoVh4KN9BU3HCYDtgtENPA==} + + '@tiptap/react@3.6.5': + resolution: {integrity: sha512-kum9fYzY6qmHuabcXDUTX2sVLdtJtZS0kN91mwD29Ue8HUkjVvEX92PwV2HtgNw3WFMaVxgm/dtm3XPTAlUEwg==} + peerDependencies: + '@tiptap/core': ^3.6.5 + '@tiptap/pm': ^3.6.5 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tiptap/starter-kit@3.6.5': + resolution: {integrity: sha512-LNAJQstB/VazmMlRbUyu3rCNVQ9af25Ywkn3Uyuwt3Ks9ZlliIm/x/zertdXTY2adoig+b36zT5Xcx1O4IdJ3A==} + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1523,6 +1698,15 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/node@20.19.19': resolution: {integrity: sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==} @@ -1534,6 +1718,12 @@ packages: '@types/react@19.2.0': resolution: {integrity: sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==} + '@types/sanitize-html@2.16.0': + resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@typescript-eslint/eslint-plugin@8.45.0': resolution: {integrity: sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1910,6 +2100,9 @@ packages: react: optional: true + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2010,6 +2203,10 @@ packages: resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} engines: {node: '>=16.0.0'} + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -2038,6 +2235,19 @@ packages: dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -2060,6 +2270,10 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -2393,6 +2607,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2484,6 +2701,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2640,6 +2861,12 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + linkifyjs@4.3.2: + resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2665,10 +2892,17 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2785,6 +3019,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -2801,6 +3038,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-srcset@1.0.2: + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2873,6 +3113,68 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prosemirror-changeset@2.3.1: + resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} + + prosemirror-collab@1.3.1: + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} + + prosemirror-gapcursor@1.3.2: + resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==} + + prosemirror-history@1.4.1: + resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} + + prosemirror-inputrules@1.5.0: + resolution: {integrity: sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-markdown@1.13.2: + resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==} + + prosemirror-menu@1.2.5: + resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==} + + prosemirror-model@1.25.3: + resolution: {integrity: sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==} + + prosemirror-schema-basic@1.2.4: + resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.3: + resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} + + prosemirror-tables@1.8.1: + resolution: {integrity: sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==} + + prosemirror-trailing-node@3.0.0: + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} + peerDependencies: + prosemirror-model: ^1.22.1 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.33.8 + + prosemirror-transform@1.10.4: + resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==} + + prosemirror-view@1.41.2: + resolution: {integrity: sha512-PGS/jETmh+Qjmre/6vcG7SNHAKiGc4vKOJmHMPRmvcUl7ISuVtrtHmH06UDUwaim4NDJfZfVMl7U7JkMMETa6g==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2996,6 +3298,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3011,6 +3316,9 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + sanitize-html@2.17.0: + resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==} + scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} @@ -3227,6 +3535,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -3335,6 +3646,9 @@ packages: jsdom: optional: true + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -4297,6 +4611,8 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@remirror/core-constants@3.0.0': {} + '@rollup/rollup-android-arm-eabi@4.52.4': optional: true @@ -4460,6 +4776,187 @@ snapshots: '@tanstack/table-core@8.21.3': {} + '@tiptap/core@3.6.5(@tiptap/pm@3.6.5)': + dependencies: + '@tiptap/pm': 3.6.5 + + '@tiptap/extension-blockquote@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-bold@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-bubble-menu@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + optional: true + + '@tiptap/extension-bullet-list@3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extension-list': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-code-block@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + + '@tiptap/extension-code@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-document@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-dropcursor@3.6.5(@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extensions': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-floating-menu@3.6.5(@floating-ui/dom@1.7.4)(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + optional: true + + '@tiptap/extension-gapcursor@3.6.5(@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extensions': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-hard-break@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-heading@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-horizontal-rule@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + + '@tiptap/extension-italic@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-link@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + linkifyjs: 4.3.2 + + '@tiptap/extension-list-item@3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extension-list': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-list-keymap@3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extension-list': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + + '@tiptap/extension-ordered-list@3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extension-list': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-paragraph@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-placeholder@3.6.5(@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/extensions': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + + '@tiptap/extension-strike@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-text@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extension-underline@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + + '@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + + '@tiptap/pm@3.6.5': + dependencies: + prosemirror-changeset: 2.3.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.3.2 + prosemirror-history: 1.4.1 + prosemirror-inputrules: 1.5.0 + prosemirror-keymap: 1.2.3 + prosemirror-markdown: 1.13.2 + prosemirror-menu: 1.2.5 + prosemirror-model: 1.25.3 + prosemirror-schema-basic: 1.2.4 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.3 + prosemirror-tables: 1.8.1 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.41.2) + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.2 + + '@tiptap/react@3.6.5(@floating-ui/dom@1.7.4)(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + '@types/react': 19.2.0 + '@types/react-dom': 19.2.0(@types/react@19.2.0) + '@types/use-sync-external-store': 0.0.6 + fast-deep-equal: 3.1.3 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.1.0) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/extension-floating-menu': 3.6.5(@floating-ui/dom@1.7.4)(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + transitivePeerDependencies: + - '@floating-ui/dom' + + '@tiptap/starter-kit@3.6.5': + dependencies: + '@tiptap/core': 3.6.5(@tiptap/pm@3.6.5) + '@tiptap/extension-blockquote': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-bold': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-bullet-list': 3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/extension-code': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-code-block': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/extension-document': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-dropcursor': 3.6.5(@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/extension-gapcursor': 3.6.5(@tiptap/extensions@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/extension-hard-break': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-heading': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-horizontal-rule': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/extension-italic': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-link': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/extension-list': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/extension-list-item': 3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/extension-list-keymap': 3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/extension-ordered-list': 3.6.5(@tiptap/extension-list@3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5)) + '@tiptap/extension-paragraph': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-strike': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-text': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extension-underline': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5)) + '@tiptap/extensions': 3.6.5(@tiptap/core@3.6.5(@tiptap/pm@3.6.5))(@tiptap/pm@3.6.5) + '@tiptap/pm': 3.6.5 + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -4495,6 +4992,15 @@ snapshots: '@types/json5@0.0.29': {} + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdurl@2.0.0': {} + '@types/node@20.19.19': dependencies: undici-types: 6.21.0 @@ -4507,6 +5013,12 @@ snapshots: dependencies: csstype: 3.1.3 + '@types/sanitize-html@2.16.0': + dependencies: + htmlparser2: 8.0.2 + + '@types/use-sync-external-store@0.0.6': {} + '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -4911,6 +5423,8 @@ snapshots: optionalDependencies: react: 19.1.0 + crelt@1.0.6: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4995,6 +5509,8 @@ snapshots: deepmerge-ts@7.1.5: {} + deepmerge@4.3.1: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -5024,6 +5540,24 @@ snapshots: '@babel/runtime': 7.28.4 csstype: 3.1.3 + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dotenv@16.6.1: {} dunder-proto@1.0.1: @@ -5046,6 +5580,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + entities@4.5.0: {} + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -5572,6 +6108,13 @@ snapshots: dependencies: function-bind: 1.1.2 + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + ignore@5.3.2: {} ignore@7.0.5: {} @@ -5664,6 +6207,8 @@ snapshots: is-number@7.0.0: {} + is-plain-object@5.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -5803,6 +6348,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + linkifyjs@4.3.2: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -5825,8 +6376,19 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + math-intrinsics@1.1.0: {} + mdurl@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -5949,6 +6511,8 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + orderedmap@2.1.1: {} + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -5967,6 +6531,8 @@ snapshots: dependencies: callsites: 3.1.0 + parse-srcset@1.0.2: {} + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -6026,6 +6592,111 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + prosemirror-changeset@2.3.1: + dependencies: + prosemirror-transform: 1.10.4 + + prosemirror-collab@1.3.1: + dependencies: + prosemirror-state: 1.4.3 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.2 + + prosemirror-gapcursor@1.3.2: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-view: 1.41.2 + + prosemirror-history@1.4.1: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.2 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.0: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.3 + w3c-keyname: 2.2.8 + + prosemirror-markdown@1.13.2: + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + prosemirror-model: 1.25.3 + + prosemirror-menu@1.2.5: + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.7.1 + prosemirror-history: 1.4.1 + prosemirror-state: 1.4.3 + + prosemirror-model@1.25.3: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-basic@1.2.4: + dependencies: + prosemirror-model: 1.25.3 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-state@1.4.3: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.2 + + prosemirror-tables@1.8.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.2 + + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.41.2): + dependencies: + '@remirror/core-constants': 3.0.0 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-view: 1.41.2 + + prosemirror-transform@1.10.4: + dependencies: + prosemirror-model: 1.25.3 + + prosemirror-view@1.41.2: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + punycode.js@2.3.1: {} + punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -6181,6 +6852,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.52.4 fsevents: 2.3.3 + rope-sequence@1.3.4: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -6204,6 +6877,15 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + sanitize-html@2.17.0: + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.5.6 + scheduler@0.26.0: {} semver@6.3.1: {} @@ -6472,6 +7154,8 @@ snapshots: typescript@5.9.3: {} + uc.micro@2.1.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -6617,6 +7301,8 @@ snapshots: - supports-color - terser + w3c-keyname@2.2.8: {} + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/web/src/app/globals.css b/web/src/app/globals.css index d378588..997bf7c 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -112,11 +112,29 @@ --sidebar-ring: oklch(0.71 0.15 254.6); } -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground font-sans antialiased; - } -} +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground font-sans antialiased; + } +} + +@layer components { + /* Tipografia básica para conteúdos rich text (Tiptap) */ + .rich-text { + @apply text-foreground; + } + .rich-text p { @apply my-2; } + .rich-text a { @apply text-primary underline; } + .rich-text ul { @apply my-2 list-disc ps-5; } + .rich-text ol { @apply my-2 list-decimal ps-5; } + .rich-text li { @apply my-1; } + .rich-text blockquote { @apply my-3 border-l-2 border-muted-foreground/30 ps-3 text-muted-foreground; } + .rich-text h1 { @apply text-xl font-semibold my-3; } + .rich-text h2 { @apply text-lg font-semibold my-3; } + .rich-text h3 { @apply text-base font-semibold my-2; } + .rich-text code { @apply rounded bg-muted px-1 py-0.5 text-xs; } + .rich-text pre { @apply my-3 overflow-x-auto rounded bg-muted p-3 text-xs; } +} diff --git a/web/src/app/tickets/[id]/page.tsx b/web/src/app/tickets/[id]/page.tsx index e4d8224..b150aee 100644 --- a/web/src/app/tickets/[id]/page.tsx +++ b/web/src/app/tickets/[id]/page.tsx @@ -3,6 +3,7 @@ import { SiteHeader } from "@/components/site-header" import { TicketDetailView } from "@/components/tickets/ticket-detail-view" import { TicketDetailStatic } from "@/components/tickets/ticket-detail-static" import { getTicketById } from "@/lib/mocks/tickets" +import type { TicketWithDetails } from "@/lib/schemas/ticket" type TicketDetailPageProps = { params: Promise<{ id: string }> @@ -24,7 +25,7 @@ export default async function TicketDetailPage({ params }: TicketDetailPageProps /> } > - {isMock && mock ? : } + {isMock && mock ? : } ) } diff --git a/web/src/app/tickets/new/page.tsx b/web/src/app/tickets/new/page.tsx index 1354e53..e7674c9 100644 --- a/web/src/app/tickets/new/page.tsx +++ b/web/src/app/tickets/new/page.tsx @@ -1,6 +1,8 @@ "use client"; import { useMemo, useState } from "react"; +import type { Id } from "@/convex/_generated/dataModel"; +import type { TicketQueueSummary } from "@/lib/schemas/ticket"; import { useRouter } from "next/navigation"; import { useMutation, useQuery } from "convex/react"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -8,12 +10,14 @@ import { useMutation, useQuery } from "convex/react"; import { api } from "@/convex/_generated/api"; import { DEFAULT_TENANT_ID } from "@/lib/constants"; import { useAuth } from "@/lib/auth-client"; +import { RichTextEditor } from "@/components/ui/rich-text-editor"; export default function NewTicketPage() { const router = useRouter(); const { userId } = useAuth(); - const queues = useQuery(api.queues.summary, { tenantId: DEFAULT_TENANT_ID }) ?? []; + const queues = (useQuery(api.queues.summary, { tenantId: DEFAULT_TENANT_ID }) as TicketQueueSummary[] | undefined) ?? []; const create = useMutation(api.tickets.create); + const addComment = useMutation(api.tickets.addComment); const ensureDefaults = useMutation(api.bootstrap.ensureDefaults); const [subject, setSubject] = useState(""); @@ -21,25 +25,30 @@ export default function NewTicketPage() { const [priority, setPriority] = useState("MEDIUM"); const [channel, setChannel] = useState("MANUAL"); const [queueName, setQueueName] = useState(null); + const [description, setDescription] = useState(""); - const queueOptions = useMemo(() => queues.map((q: any) => q.name), [queues]); + const queueOptions = useMemo(() => queues.map((q) => q.name), [queues]); async function submit(e: React.FormEvent) { e.preventDefault(); if (!userId) return; if (queues.length === 0) await ensureDefaults({ tenantId: DEFAULT_TENANT_ID }); // Encontrar a fila pelo nome (simples) - const selQueue = (queues as any[]).find((q: any) => q.name === queueName); - const queueId = selQueue ? selQueue.id : undefined; + const selQueue = queues.find((q) => q.name === queueName); + const queueId = selQueue ? (selQueue.id as Id<"queues">) : undefined; const id = await create({ tenantId: DEFAULT_TENANT_ID, subject, summary, priority, channel, - queueId: queueId as any, - requesterId: userId as any, + queueId, + requesterId: userId as Id<"users">, }); + const hasDescription = description.replace(/<[^>]*>/g, "").trim().length > 0 + if (hasDescription) { + await addComment({ ticketId: id as Id<"tickets">, authorId: userId as Id<"users">, visibility: "PUBLIC", body: description, attachments: [] }) + } router.replace(`/tickets/${id}`); } @@ -55,6 +64,10 @@ export default function NewTicketPage() {