diff --git a/apps/web/index.html b/apps/web/index.html
index fc8d2c817..8a8ae51f5 100644
--- a/apps/web/index.html
+++ b/apps/web/index.html
@@ -23,10 +23,7 @@
-
+
Loading
diff --git a/apps/web/package.json b/apps/web/package.json
index 77d09a82c..242345739 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -76,6 +76,7 @@
"react-resizable-panels": "^4.12.1",
"react-window": "^2.2.7",
"react-zoom-pan-pinch": "^4.0.3",
+ "remark-gfm": "^4.0.1",
"sonner": "^2.0.7",
"ts-pattern": "^5.9.0",
"usehooks-ts": "^3.1.1",
diff --git a/apps/web/src/components/layout/loading-screen.test.tsx b/apps/web/src/components/layout/loading-screen.test.tsx
index 03f0630b0..703371cd2 100644
--- a/apps/web/src/components/layout/loading-screen.test.tsx
+++ b/apps/web/src/components/layout/loading-screen.test.tsx
@@ -1,17 +1,27 @@
// @vitest-environment happy-dom
+import { readFileSync } from "node:fs";
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { LoadingScreen } from "./loading-screen";
describe("LoadingScreen", () => {
- it("renders the Reactive Resume logo and spinner", () => {
+ it("renders the Reactive Resume icon and spinner", () => {
render();
- expect(screen.getAllByAltText("Reactive Resume")).toHaveLength(2);
+ const icons = screen.getAllByAltText("Reactive Resume");
+ expect(icons).toHaveLength(2);
+ expect(icons.map((icon) => icon.getAttribute("src"))).toEqual(["/icon/dark.svg", "/icon/light.svg"]);
expect(screen.getByLabelText("Loading")).toBeInTheDocument();
});
+ it("uses the same icon asset as the initial HTML loader", () => {
+ const html = readFileSync("index.html", "utf8");
+
+ expect(html).toContain('src="/icon/dark.svg"');
+ expect(html).not.toContain('src="/logo/dark.svg"');
+ });
+
it("fills the viewport (fixed inset-0)", () => {
const { container } = render();
diff --git a/apps/web/src/components/layout/loading-screen.tsx b/apps/web/src/components/layout/loading-screen.tsx
index df90b84c9..5c084b2a5 100644
--- a/apps/web/src/components/layout/loading-screen.tsx
+++ b/apps/web/src/components/layout/loading-screen.tsx
@@ -4,7 +4,7 @@ import { Spinner } from "@reactive-resume/ui/components/spinner";
export function LoadingScreen() {
return (
-
+
);
diff --git a/apps/web/src/features/command-palette/pages/resumes.test.tsx b/apps/web/src/features/command-palette/pages/resumes.test.tsx
new file mode 100644
index 000000000..1729fc6a7
--- /dev/null
+++ b/apps/web/src/features/command-palette/pages/resumes.test.tsx
@@ -0,0 +1,74 @@
+// @vitest-environment happy-dom
+
+import { render, screen } from "@testing-library/react";
+import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
+import { i18n } from "@lingui/core";
+import { I18nProvider } from "@lingui/react";
+import { useQuery } from "@tanstack/react-query";
+import { Command, CommandList } from "@reactive-resume/ui/components/command";
+import { useCommandPaletteStore } from "../store";
+
+const queryOptionsMock = vi.fn((options?: unknown) => options ?? {});
+
+vi.mock("@tanstack/react-query", () => ({
+ useQuery: vi.fn(),
+}));
+
+vi.mock("@tanstack/react-router", () => ({
+ useNavigate: () => vi.fn(),
+ useRouteContext: () => ({ session: { user: { id: "user-1" } } }),
+}));
+
+vi.mock("@/dialogs/store", () => ({
+ useDialogStore: () => ({ openDialog: vi.fn() }),
+}));
+
+vi.mock("@/libs/orpc/client", () => ({
+ orpc: {
+ resume: {
+ list: {
+ queryOptions: queryOptionsMock,
+ },
+ },
+ },
+}));
+
+const { ResumesCommandGroup } = await import("./resumes");
+
+beforeAll(() => {
+ i18n.loadAndActivate({ locale: "en", messages: {} });
+});
+
+afterEach(() => {
+ vi.clearAllMocks();
+ useCommandPaletteStore.setState({ open: false, search: "", pages: [] });
+});
+
+const renderGroup = () =>
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+describe("ResumesCommandGroup", () => {
+ it("loads resumes with the default list input on the resumes page", () => {
+ useCommandPaletteStore.setState({ pages: ["resumes"] });
+ vi.mocked(useQuery).mockReturnValue({
+ data: [{ id: "resume-1", name: "Evil Apricot Pike" }],
+ isLoading: false,
+ } as ReturnType);
+
+ renderGroup();
+
+ expect(queryOptionsMock).toHaveBeenCalledWith({
+ enabled: true,
+ input: { sort: "lastUpdatedAt", tags: [] },
+ });
+ expect(screen.getByText("Evil Apricot Pike")).toBeInTheDocument();
+ });
+});
diff --git a/apps/web/src/features/command-palette/pages/resumes.tsx b/apps/web/src/features/command-palette/pages/resumes.tsx
index d9de7a5b4..70ea4a756 100644
--- a/apps/web/src/features/command-palette/pages/resumes.tsx
+++ b/apps/web/src/features/command-palette/pages/resumes.tsx
@@ -24,6 +24,7 @@ export function ResumesCommandGroup() {
const { data: resumes, isLoading } = useQuery(
orpc.resume.list.queryOptions({
enabled: !!session && isResumesPage,
+ input: { sort: "lastUpdatedAt", tags: [] },
}),
);
diff --git a/apps/web/src/routes/agent/-components/agent-chat.test.tsx b/apps/web/src/routes/agent/-components/agent-chat.test.tsx
new file mode 100644
index 000000000..2b2670874
--- /dev/null
+++ b/apps/web/src/routes/agent/-components/agent-chat.test.tsx
@@ -0,0 +1,25 @@
+// @vitest-environment happy-dom
+
+import { render, screen, within } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+import { AssistantMarkdown } from "./agent-chat";
+
+describe("AssistantMarkdown", () => {
+ it("renders GitHub-style pipe tables as tables", () => {
+ render(
+ ,
+ );
+
+ const table = screen.getByRole("table");
+
+ expect(within(table).getByRole("columnheader", { name: "Weak/Generic" })).toBeInTheDocument();
+ expect(within(table).getByRole("columnheader", { name: "Improved Version" })).toBeInTheDocument();
+ expect(within(table).getByRole("cell", { name: "Mentored 3+ juniors" })).toBeInTheDocument();
+ expect(screen.getByText("Before")).toBeInTheDocument();
+ expect(screen.getByText("After")).toBeInTheDocument();
+ });
+});
diff --git a/apps/web/src/routes/agent/-components/agent-chat.tsx b/apps/web/src/routes/agent/-components/agent-chat.tsx
index 8a979f6c7..52ab33031 100644
--- a/apps/web/src/routes/agent/-components/agent-chat.tsx
+++ b/apps/web/src/routes/agent/-components/agent-chat.tsx
@@ -27,6 +27,7 @@ import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
import { m } from "motion/react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
import { toast } from "sonner";
import {
Attachment,
@@ -380,10 +381,11 @@ function getMessagePartKey(messageId: string, part: UIMessage["parts"][number])
return `${messageId}-${part.type}-${JSON.stringify(part)}`;
}
-function AssistantMarkdown({ text }: AssistantMarkdownProps) {
+export function AssistantMarkdown({ text }: AssistantMarkdownProps) {
return (
{children}
,
ul: ({ children }) => ,
@@ -407,6 +409,13 @@ function AssistantMarkdown({ text }: AssistantMarkdownProps) {
blockquote: ({ children }) => (
{children}
),
+ table: ({ children }) => (
+
+ ),
+ th: ({ children }) => {children} | ,
+ td: ({ children }) => {children} | ,
}}
>
{text}
diff --git a/packages/api/src/features/agent/resume.test.ts b/packages/api/src/features/agent/resume.test.ts
index 061340ef8..816e739c5 100644
--- a/packages/api/src/features/agent/resume.test.ts
+++ b/packages/api/src/features/agent/resume.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
-import { buildAgentDraftResumeName, buildUniqueAgentDraftSlug } from "./resume";
+import { buildAgentDraftResumeName, buildUniqueAgentDraftSlug, normalizeAgentResumePatchOperations } from "./resume";
describe("agent resume setup helpers", () => {
it("names duplicated resumes as AI drafts", () => {
@@ -14,3 +14,25 @@ describe("agent resume setup helpers", () => {
);
});
});
+
+describe("normalizeAgentResumePatchOperations", () => {
+ it("prefixes shorthand standard section paths without touching custom section paths", () => {
+ const result = normalizeAgentResumePatchOperations(
+ {
+ sections: {
+ experience: {},
+ education: {},
+ },
+ },
+ [
+ { op: "replace", path: "/experience/items/0/description", value: "Updated" },
+ { op: "copy", from: "/education/items/0", path: "/customSections/0/items/-" },
+ ],
+ );
+
+ expect(result).toEqual([
+ { op: "replace", path: "/sections/experience/items/0/description", value: "Updated" },
+ { op: "copy", from: "/sections/education/items/0", path: "/customSections/0/items/-" },
+ ]);
+ });
+});
diff --git a/packages/api/src/features/agent/resume.ts b/packages/api/src/features/agent/resume.ts
index 4a5385849..e33e5ca73 100644
--- a/packages/api/src/features/agent/resume.ts
+++ b/packages/api/src/features/agent/resume.ts
@@ -1,3 +1,4 @@
+import type { JsonPatchOperation } from "@reactive-resume/resume/patch";
import { slugify } from "@reactive-resume/utils/string";
const AI_DRAFT_SUFFIX = " - AI Draft";
@@ -23,3 +24,31 @@ export function buildUniqueAgentDraftSlug(sourceName: string, existingSlugs: Set
return candidate;
}
+
+function decodeJsonPointerSegment(segment: string) {
+ return segment.replace(/~1/g, "/").replace(/~0/g, "~");
+}
+
+function normalizeSectionShortcutPath(data: { sections: Record }, path: string) {
+ if (!path.startsWith("/") || path.startsWith("/sections/")) return path;
+
+ const sectionId = decodeJsonPointerSegment(path.slice(1).split("/")[0] ?? "");
+ if (!Object.hasOwn(data.sections, sectionId)) return path;
+
+ return `/sections${path}`;
+}
+
+export function normalizeAgentResumePatchOperations(
+ data: { sections: Record },
+ operations: JsonPatchOperation[],
+): JsonPatchOperation[] {
+ return operations.map((operation) => {
+ const path = normalizeSectionShortcutPath(data, operation.path);
+ const normalized = path === operation.path ? operation : { ...operation, path };
+
+ if (!("from" in normalized)) return normalized;
+
+ const from = normalizeSectionShortcutPath(data, normalized.from);
+ return from === normalized.from ? normalized : { ...normalized, from };
+ });
+}
diff --git a/packages/api/src/features/agent/service.ts b/packages/api/src/features/agent/service.ts
index 31642c04e..579393219 100644
--- a/packages/api/src/features/agent/service.ts
+++ b/packages/api/src/features/agent/service.ts
@@ -15,7 +15,7 @@ import { getAgentModel } from "../ai/service";
import { aiProvidersService } from "../ai-providers/service";
import { resumeService } from "../resume/service";
import { getStorageService, inferContentType } from "../storage/service";
-import { buildAgentDraftResumeName, buildUniqueAgentDraftSlug } from "./resume";
+import { buildAgentDraftResumeName, buildUniqueAgentDraftSlug, normalizeAgentResumePatchOperations } from "./resume";
import { claimActiveAgentRun, clearActiveAgentRunIfCurrent } from "./runs";
import { agentStreamLifecycle } from "./streams";
import { buildAgentInstructions, buildAgentTools } from "./tools";
@@ -717,12 +717,13 @@ async function applyResumePatch(input: {
}) {
const before = await resumeService.getById({ id: input.resumeId, userId: input.userId });
const snapshotData = cloneResumeData(before.data);
+ const operations = normalizeAgentResumePatchOperations(before.data, input.operations);
const { action, patched } = await db.transaction(async (tx) => {
const patched = await resumeService.patchInTransaction(tx, {
id: input.resumeId,
userId: input.userId,
- operations: input.operations,
+ operations,
});
const [action] = await tx
@@ -735,7 +736,7 @@ async function applyResumePatch(input: {
status: "applied",
title: input.title,
...(input.summary !== undefined ? { summary: input.summary } : {}),
- operations: input.operations,
+ operations,
snapshotData,
baseUpdatedAt: before.updatedAt,
appliedUpdatedAt: patched.updatedAt,
@@ -787,10 +788,14 @@ function createAgent(input: {
patchRoot: "data",
patchPathExamples: {
visibleName: "/basics/name",
+ standardExperienceDescription: "/sections/experience/items/0/description",
+ customSectionDescription: "/customSections/0/items/0/description",
},
patchNotes: [
"apply_resume_patch paths are rooted at the `data` object below.",
"Do not prefix paths with `/data`.",
+ "Built-in sections live under `/sections/`, for example `/sections/experience/items/0/description`.",
+ "Custom sections live under `/customSections/`, even when their `type` is `experience`, `education`, or another built-in section type.",
"The resume file/title `name` metadata is read-only for apply_resume_patch.",
],
data: resume.data,
diff --git a/packages/api/src/features/agent/tools.test.ts b/packages/api/src/features/agent/tools.test.ts
index 1acfb276b..5251cc980 100644
--- a/packages/api/src/features/agent/tools.test.ts
+++ b/packages/api/src/features/agent/tools.test.ts
@@ -90,6 +90,12 @@ describe("agent tools", () => {
);
expect(buildAgentInstructions({ hasProviderNativeSearch: false })).toContain("Batch related JSON Patch operations");
expect(buildAgentInstructions({ hasProviderNativeSearch: false })).toContain("/basics/name");
+ expect(buildAgentInstructions({ hasProviderNativeSearch: false })).toContain(
+ "/sections/experience/items/0/description",
+ );
+ expect(buildAgentInstructions({ hasProviderNativeSearch: false })).toContain(
+ "/customSections/0/items/0/description",
+ );
expect(buildAgentInstructions({ hasProviderNativeSearch: false })).toContain("never /data/basics/name or /name");
expect(buildAgentInstructions({ hasProviderNativeSearch: false })).toContain("clean Markdown");
});
diff --git a/packages/api/src/features/agent/tools.ts b/packages/api/src/features/agent/tools.ts
index b0122be27..89a01ad8d 100644
--- a/packages/api/src/features/agent/tools.ts
+++ b/packages/api/src/features/agent/tools.ts
@@ -52,7 +52,7 @@ function buildProviderNativeAgentTools(provider: AgentProviderConfig): ToolSet {
export function buildAgentInstructions({ hasProviderNativeSearch }: { hasProviderNativeSearch: boolean }) {
const baseInstructions =
- "You are an expert resume-writing agent inside Reactive Resume. Help the user improve the working resume for a target role. Read the resume before editing. Respond to the user in clean Markdown with concise paragraphs, bullets, and bold text when it improves scanability. Apply concise, valid JSON Patch operations when changes are useful. Patch paths are evaluated against the resume data object returned by read_resume, so use paths like /basics/name for the visible name and never /data/basics/name or /name. apply_resume_patch cannot rename the resume file/title metadata. Batch related JSON Patch operations into one apply_resume_patch call for each coherent edit instead of making repeated patch calls for the same request. Ask the user a question when a missing preference blocks a high-confidence edit.";
+ "You are an expert resume-writing agent inside Reactive Resume. Help the user improve the working resume for a target role. Read the resume before editing. Respond to the user in clean Markdown with concise paragraphs, bullets, and bold text when it improves scanability. Apply concise, valid JSON Patch operations when changes are useful. Patch paths are evaluated against the resume data object returned by read_resume, so use paths like /basics/name for the visible name and never /data/basics/name or /name. Built-in sections must use /sections/, for example /sections/experience/items/0/description. Custom sections must use /customSections/, for example /customSections/0/items/0/description, even when their type is experience, education, or another built-in section type. apply_resume_patch cannot rename the resume file/title metadata. Batch related JSON Patch operations into one apply_resume_patch call for each coherent edit instead of making repeated patch calls for the same request. Ask the user a question when a missing preference blocks a high-confidence edit.";
if (!hasProviderNativeSearch) {
return `${baseInstructions} Live web research is unavailable with the selected provider or model. If the user asks you to browse, search the web, fetch a URL, or use current online context, briefly tell them live web research is unavailable with the selected provider/model and ask them to paste or attach the relevant content. Continue normal resume editing using the resume, chat context, and attachments.`;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 480c19691..548bbbd7e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -454,6 +454,9 @@ importers:
react-zoom-pan-pinch:
specifier: ^4.0.3
version: 4.0.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
@@ -5907,6 +5910,9 @@ packages:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
marked@15.0.12:
resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==}
engines: {node: '>= 18'}
@@ -5916,9 +5922,30 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
mdast-util-from-markdown@2.0.3:
resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
mdast-util-mdx-expression@2.0.1:
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
@@ -5975,6 +6002,27 @@ packages:
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.1:
+ resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
micromark-factory-destination@2.0.1:
resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
@@ -6747,12 +6795,18 @@ packages:
resolution: {integrity: sha512-GP6h9OgJmhAZpb3dbNbXTfRWVnGcoMhWRZv/HxgM4/qCVqs1P9ukQdYxaUhjWBSAs9oJ/uPXUUvGT1VMe0Bs0Q==}
engines: {node: '>=16'}
+ remark-gfm@4.0.1:
+ resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
+
remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
remark-rehype@11.1.2:
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
+ remark-stringify@11.0.0:
+ resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -12232,10 +12286,19 @@ snapshots:
dependencies:
semver: 7.8.5
+ markdown-table@3.0.4: {}
+
marked@15.0.12: {}
math-intrinsics@1.1.0: {}
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
mdast-util-from-markdown@2.0.3:
dependencies:
'@types/mdast': 4.0.4
@@ -12253,6 +12316,63 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
mdast-util-mdx-expression@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
@@ -12362,6 +12482,64 @@ snapshots:
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.1
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
micromark-factory-destination@2.0.1:
dependencies:
micromark-util-character: 2.1.1
@@ -13252,6 +13430,17 @@ snapshots:
regexp-util@2.0.3: {}
+ remark-gfm@4.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-gfm: 3.1.0
+ micromark-extension-gfm: 3.0.0
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
remark-parse@11.0.0:
dependencies:
'@types/mdast': 4.0.4
@@ -13269,6 +13458,12 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
+ remark-stringify@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-to-markdown: 2.1.2
+ unified: 11.0.5
+
require-directory@2.1.1:
optional: true