Skip to content

Commit

Permalink
mdx extension
Browse files Browse the repository at this point in the history
  • Loading branch information
areknawo committed Oct 9, 2023
1 parent e165896 commit ee9e404
Show file tree
Hide file tree
Showing 25 changed files with 205 additions and 62 deletions.
6 changes: 3 additions & 3 deletions apps/backend/extensions/src/extensions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { extensionsRouter } from "./routes";
import { publicPlugin, createContext } from "@vrite/backend";
import { publicPlugin, createFastifyContext } from "@vrite/backend";
import {
createOpenApiNodeHttpHandler,
CreateOpenApiNodeHttpHandlerOptions
Expand Down Expand Up @@ -56,15 +56,15 @@ const fastifyTRPCOpenApiPlugin = <TRouter extends AnyRouter>(
};
const extensionsService = publicPlugin(async (fastify) => {
await fastify.register(corsPlugin, {
credentials: true,
methods: ["GET", "DELETE", "PUT", "POST"],
credentials: true,
origin: true
});
await fastify.register(fastifyTRPCOpenApiPlugin, {
basePath: "/",
router: extensionsRouter,
createContext({ req, res }: { req: FastifyRequest; res: FastifyReply }) {
return createContext({ req, res }, fastify);
return createFastifyContext({ req, res }, fastify);
}
});
});
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/extensions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { extensionsService } from "./extensions";
import { createServer, z } from "@vrite/backend";
import { createServer } from "@vrite/backend";

(async () => {
const server = await createServer({});
Expand Down
5 changes: 1 addition & 4 deletions apps/backend/extensions/src/routes/mdx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { mdxAsyncInputTransformer } from "./input-transformer";
import { mdxAsyncOutputTransformer } from "./output-transformer";
import { procedure, router, z } from "@vrite/backend";
import { InputTransformer } from "@vrite/sdk/transformers";
import { OpenAI } from "openai";

// test

const basePath = "/mdx";
const mdxRouter = router({
Expand All @@ -24,7 +21,7 @@ const mdxRouter = router({
})
)
)
.mutation(async ({ ctx, input }) => {
.mutation(async ({ input }) => {
const output: Array<ReturnType<InputTransformer>> = [];

for await (const content of input.data) {
Expand Down
19 changes: 17 additions & 2 deletions apps/backend/extensions/src/routes/mdx/input-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ const mdxAsyncInputTransformer = async (input: string): Promise<ReturnType<Input
"data-element": "true",
"data-type": "Import"
},
children: [defaultHandlers.code(state, { type: "code", lang: "mdx", value: node.value })]
children: [
defaultHandlers.code(state, { type: "code", lang: "mdx", value: node.value.trim() })
]
};

state.patch(node, result);
Expand All @@ -92,7 +94,11 @@ const mdxAsyncInputTransformer = async (input: string): Promise<ReturnType<Input

(node.meta || "").split(" ").forEach((item) => {
if (item.startsWith("title=")) {
title = item.replace("title=", "");
const match = item.match(/title="(.+?)"/);

if (match) {
title = match[1] || title;
}
} else {
meta.push(item);
}
Expand All @@ -115,6 +121,15 @@ const mdxAsyncInputTransformer = async (input: string): Promise<ReturnType<Input

return result;
},
mdxFlowExpression() {
return undefined;
},
mdxJsxTextElement() {
return undefined;
},
mdxTextExpression() {
return undefined;
},
mdxJsxFlowElement(state, node: RootContentMap["mdxJsxFlowElement"]) {
const result: Element = {
tagName: "div",
Expand Down
31 changes: 23 additions & 8 deletions apps/backend/extensions/src/routes/mdx/output-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { format } from "prettier/standalone";
import babelPlugin from "prettier/plugins/babel";
import estreePlugin from "prettier/plugins/estree";
import markdownPlugin from "prettier/plugins/markdown";
import { ContentPiece } from "@vrite/sdk/api";
import { convert as convertToText } from "html-to-text";
import { dump } from "js-yaml";
Expand Down Expand Up @@ -140,7 +141,7 @@ const mdxAsyncOutputTransformer = async (
if (attrs.type === "Import" && elementWalker.children[0]?.node?.type === "codeBlock") {
return `${transformTextNode(
elementWalker.children[0] as JSONContentNodeWalker<JSONContentNode["codeBlock"]>
)}`;
).trim()}`;
}

const keyValueProps = Object.entries(attrs.props).map(([key, value]) => {
Expand All @@ -156,7 +157,14 @@ const mdxAsyncOutputTransformer = async (
);

if (elementWalker.children.length > 0) {
return `${openingTag}\n${transformContentNode(elementWalker)}\n</${attrs.type}>`;
return `${openingTag}\n${(
await transformContentNode(
elementWalker as JSONContentNodeWalker<JSONContentNode["element"]>
)
)
.split("\n")
.map((line) => ` ${line}`)
.join("\n")}\n</${attrs.type}>`;
}

return openingTag;
Expand Down Expand Up @@ -184,7 +192,7 @@ const mdxAsyncOutputTransformer = async (
case "bulletList":
case "orderedList":
case "taskList":
return `${isPreviousSiblingList ? "\n" : ""}${transformList(
return `${isPreviousSiblingList ? "\n" : ""}${await transformList(
child as JSONContentNodeWalker<
JSONContentNode["bulletList" | "orderedList" | "taskList"]
>
Expand Down Expand Up @@ -283,18 +291,25 @@ const mdxAsyncOutputTransformer = async (
{
...(contentPiece?.canonicalLink && { canonicalLink: contentPiece.canonicalLink }),
...(contentPiece?.coverUrl && { coverUrl: contentPiece.coverUrl }),
...(contentPiece?.description && { description: convertToText(contentPiece.description) }),
...(contentPiece?.description && {
description: convertToText(contentPiece.description, { wordwrap: false })
}),
...(contentPiece?.date && { date: dayjs(contentPiece.date).format("YYYY-MM-DD") }),
...(contentPiece?.slug && { slug: contentPiece.slug }),
...(contentPiece?.title && { title: contentPiece.title }),
...(contentPiece?.customData || {})
},
{ skipInvalid: true }
{ skipInvalid: true, forceQuotes: true, quotingType: '"' }
);

return `${frontmatter ? "---" : ""}\n${frontmatter}\n${
frontmatter ? "---" : ""
}\n\n${content.trim()}`;
return (
await format(
`${frontmatter ? "---" : ""}\n${frontmatter.trim()}\n${
frontmatter ? "---" : ""
}\n\n${content.trim()}`,
{ plugins: [markdownPlugin], parser: "mdx" }
)
).trim();
};

export { mdxAsyncOutputTransformer };
3 changes: 2 additions & 1 deletion apps/web/src/context/extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const officialExtensions = {
"dev": () => import("@vrite/extensions/dev.json"),
"hashnode": () => import("@vrite/extensions/hashnode.json"),
"medium": () => import("@vrite/extensions/medium.json"),
"gpt-3.5": () => import("@vrite/extensions/gpt-3.5.json")
"gpt-3.5": () => import("@vrite/extensions/gpt-3.5.json"),
"mdx": () => import("@vrite/extensions/mdx.json")
};
const isOfficialExtension = (id: string): boolean => {
return id in officialExtensions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const BlockActionMenu: Component<BlockActionMenuProps> = (props) => {
class={clsx(
blockAction.blocks.length !== 0 &&
!blockAction.blocks.some((block) => {
return node()?.hasMarkup(props.state.editor.schema.nodes[block]);
return node()?.type.name === props.state.editor.schema.nodes[block].name;
}) &&
"hidden"
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const BlockActionMenuPlugin = Extension.create({

rangeFrom = p.start - 1;
rangeTo = p.start + p.node.nodeSize - 1;
} catch (e) {
} catch (error) {
box.style.display = "none";
}
}
Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/lib/editor/extensions/element-menu/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const ElementMenuEditor = lazy(async () => {
const onSave = async (code: string): Promise<void> => {
const tagRegex = /^<(\w+?)(?:\s|\n|\/|>)/;
const attributeRegex =
/\s(\w+?)(?:=(?:(?:{((?:.|\n|\s)+?)})|(?:"((?:.|\n|\s)+?)")|(?:'((?:.|\n|\s)+?)')))?(?=(?:(?:\s|\n)+\w+=)|(?:(?:\s|\n)+\/?>))/g;
/\s(\w+?)(?:=(?:(?:{((?:.|\n|\s)+?)})|(?:"((?:.|\n|\s)+?)")|(?:'((?:.|\n|\s)+?)')))?(?=(?:(?:\s|\n)+\w+=?)|(?:(?:\s|\n)*\/?>))/g;
const [, tag] = tagRegex.exec(code.trim()) || [];
const attributes: Record<string, any> = {};
const processAttributes = async (): Promise<void> => {
Expand Down Expand Up @@ -187,7 +187,6 @@ const ElementMenuEditor = lazy(async () => {
}

await onSave(codeEditor.getValue());
codeEditor.setValue(await processCode(editorCode()));
});
codeEditor.onKeyDown((event) => {
if (event.code === "Escape") {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/views/conflict/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const ConflictView: Component<{ monaco: typeof monaco }> = (props) => {
data-code-editor-theme={codeEditorTheme()}
/>
<Show when={!conflictData() || currentContent.loading}>
<div class="h-full w-full absolute top-0 left-0 bg-gray-100 dark:bg-gray-800 flex justify-center items-center">
<div class="h-full w-full absolute top-0 left-0 bg-gray-100 dark:bg-gray-800 flex justify-center items-center z-12">
<Show when={!conflictData()} fallback={<Loader />}>
<span class="text-2xl font-semibold text-gray-500 dark:text-gray-400">
Select a conflict to resolve
Expand Down
15 changes: 14 additions & 1 deletion apps/web/src/views/extensions/extension-card.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ExtensionIcon } from "./extension-icon";
import { mdiTune, mdiDownloadOutline } from "@mdi/js";
import { Component, Show } from "solid-js";
import { ExtensionDetails, hasPermission, useClient } from "#context";
import { ExtensionDetails, hasPermission, useClient, useExtensions } from "#context";
import { Card, Heading, IconButton } from "#components/primitives";

interface ExtensionCardProps {
Expand All @@ -12,6 +12,7 @@ interface ExtensionCardProps {

const ExtensionCard: Component<ExtensionCardProps> = (props) => {
const client = useClient();
const { callFunction } = useExtensions();

return (
<Card class="m-0 gap-1 flex flex-col justify-center items-center" color="contrast">
Expand Down Expand Up @@ -41,6 +42,18 @@ const ExtensionCard: Component<ExtensionCardProps> = (props) => {
permissions: props.extension.spec.permissions || []
}
});
const onConfigureCallback = props.extension.spec.lifecycle?.["on:configure"];

if (onConfigureCallback) {
await callFunction(props.extension.spec, onConfigureCallback, {
extensionId: id,
token,
context: () => ({
config: {},
spec: props.extension.spec
})
});
}

props.setOpenedExtension({ ...props.extension, config: {}, id, token });
}
Expand Down
11 changes: 10 additions & 1 deletion apps/web/src/views/git/sync-view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const extractFileName = (path: string): { fileName: string; directory: string }
return { fileName, directory };
};
const InitialSyncCard: Component = () => {
const { notify } = useNotifications();
const [loading, setLoading] = createSignal(false);
const client = useClient();

Expand All @@ -70,7 +71,14 @@ const InitialSyncCard: Component = () => {
loading={loading()}
onClick={async () => {
setLoading(true);
await client.git.github.initialSync.mutate();

try {
await client.git.github.initialSync.mutate();
notify({ text: "Latest content pulled", type: "success" });
} catch (error) {
notify({ text: "Couldn't pull content", type: "error" });
}

setLoading(false);
}}
>
Expand Down Expand Up @@ -108,6 +116,7 @@ const CommitCard: Component<{ changedRecords: App.GitRecord[] }> = (props) => {

if (status === "committed") {
notify({ text: "Changes committed", type: "success" });
setMessage("");
} else {
notify({
text: "Pull required before committing changes",
Expand Down
53 changes: 31 additions & 22 deletions apps/web/src/views/settings/transformers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,26 @@ import {
on,
onCleanup
} from "solid-js";
import { mdiFormatListBulleted, mdiPlusCircle, mdiTrashCan, mdiTune } from "@mdi/js";
import { mdiFormatListBulleted, mdiPlusCircle, mdiPuzzle, mdiTrashCan, mdiTune } from "@mdi/js";
import { createStore } from "solid-js/store";
import { TitledCard } from "#components/fragments";
import { App, hasPermission, useClient, useNotifications } from "#context";
import { Button, Card, Heading, IconButton, Loader, Tooltip } from "#components/primitives";

interface TransformerDetailsProps {
transformer: App.Transformer & { inUse?: boolean };
transformer: App.Transformer & { inUse?: boolean; extension?: boolean };
onEdit?(): void;
onDelete?(): void;
}

const useTransformers = (): {
loading: Accessor<boolean>;
transformers(): Array<App.Transformer & { inUse?: boolean }>;
transformers(): Array<App.Transformer & { inUse?: boolean; extension?: boolean }>;
} => {
const client = useClient();
const [loading, setLoading] = createSignal(false);
const [state, setState] = createStore<{
transformers: Array<App.Transformer>;
transformers: Array<App.Transformer & { inUse?: boolean; extension?: boolean }>;
}>({
transformers: []
});
Expand Down Expand Up @@ -72,25 +72,34 @@ const TransformerDetails: Component<TransformerDetailsProps> = (props) => {
<Heading level={2} class="break-anywhere flex-1">
{props.transformer.label}
</Heading>
<Show when={hasPermission("manageWorkspace")}>
<div class="flex gap-2">
<Tooltip text="Delete" class="mt-1" enabled={!props.transformer.inUse}>
<IconButton
path={mdiTrashCan}
loading={loading()}
disabled={props.transformer.inUse}
class="m-0"
text="soft"
onClick={async () => {
setLoading(true);
await client.transformers.delete.mutate({ id: props.transformer.id });
setLoading(false);
props.onDelete?.();
notify({ text: "Transformer deleted", type: "success" });
}}
/>
<Show
when={!props.transformer.extension}
fallback={
<Tooltip text="Extension" class="mt-1">
<IconButton path={mdiPuzzle} text="soft" class="m-0" badge />
</Tooltip>
</div>
}
>
<Show when={hasPermission("manageWorkspace")}>
<div class="flex gap-2">
<Tooltip text="Delete" class="mt-1" enabled={!props.transformer.inUse}>
<IconButton
path={mdiTrashCan}
loading={loading()}
disabled={props.transformer.inUse}
class="m-0"
text="soft"
onClick={async () => {
setLoading(true);
await client.transformers.delete.mutate({ id: props.transformer.id });
setLoading(false);
props.onDelete?.();
notify({ text: "Transformer deleted", type: "success" });
}}
/>
</Tooltip>
</div>
</Show>
</Show>
</Card>
);
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/database/transformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Transformer<ID extends string | ObjectId = string>
}
interface FullTransformer<ID extends string | ObjectId = string> extends Transformer<ID> {
workspaceId: ID;
extensionId?: ID;
}

const getTransformersCollection = (db: Db): Collection<UnderscoreID<FullTransformer<ObjectId>>> => {
Expand Down
Loading

0 comments on commit ee9e404

Please sign in to comment.