Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"tailwindcss-scoped-preflight": "^3.4.12",
"typescript": "^5.1.6",
"vite": "^4.5.6",
"vite-plugin-chunk-split": "^0.5.0",
"vite-plugin-node-polyfills": "^0.9.0",
"vite-plugin-node-stdlib-browser": "^0.2.1",
"vitest": "^1.6.0"
Expand Down
154 changes: 77 additions & 77 deletions src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
import { debounce } from "ts-debounce";
import { ModelManager } from "@accordproject/concerto-core";
import { TemplateMarkInterpreter } from "@accordproject/template-engine";
import { TemplateMarkTransformer } from "@accordproject/markdown-template";
import { transform } from "@accordproject/markdown-transform";
import { SAMPLES, Sample } from "../samples";
import * as playground from "../samples/playground";
import { compress, decompress } from "../utils/compression/compression";
import { AIConfig, ChatState } from '../types/components/AIAssistant.types';

Expand Down Expand Up @@ -64,31 +59,58 @@ export interface DecompressedData {
agreementHtml: string;
}

const rebuildDeBounce = debounce(rebuild, 500);
async function loadMarkdownProcessingLibs() {
try {
const [{ ModelManager }, { TemplateMarkInterpreter }, { TemplateMarkTransformer }, { transform }] =
await Promise.all([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nitro56565 good implementation, just that lets say even if once fails then due to promise.all, all the import should fail. Can we use promise.allSettled in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Owaiseimdad If any one of the packages fail then there is no reason to hold up with other packages as they all are dependent on each other and for fallback I have put a catch block to handle the errors

import("@accordproject/concerto-core"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not scalable, use a array and create a map of it inside the promise.
Ex:
`
var mod = [
"@accordproject/concerto-core",
"@accordproject/template-engine",
... more
]

await Promise.all(mod.map(module => import(module)))
`

Something like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sanketshevkar do we have any plans of scaling up on core packages, I am not sure if we need to implement it by mapping each package?

import("@accordproject/template-engine"),
import("@accordproject/markdown-template"),
import("@accordproject/markdown-transform"),
]);

async function rebuild(template: string, model: string, dataString: string) {
const modelManager = new ModelManager({ strict: true });
modelManager.addCTOModel(model, undefined, true);
await modelManager.updateExternalModels();
const engine = new TemplateMarkInterpreter(modelManager, {});
const templateMarkTransformer = new TemplateMarkTransformer();
const templateMarkDom = templateMarkTransformer.fromMarkdownTemplate(
{ content: template },
modelManager,
"contract",
{ verbose: false }
);
const data = JSON.parse(dataString);
const ciceroMark = await engine.generate(templateMarkDom, data);
return await transform(
ciceroMark.toJSON(),
"ciceromark_parsed",
["html"],
{},
{ verbose: false }
);
return { ModelManager, TemplateMarkInterpreter, TemplateMarkTransformer, transform };
} catch (error) {
console.error("Failed to load Accord processing libraries:", error);
throw new Error("Error loading Accord processing libraries");
}
}

const rebuild = debounce(async (template: string, model: string, dataString: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use throttle here? Any particular reason for debounce?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can experiment with throttling, but let's keep it for a separate issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sanketshevkar @Owaiseimdad I believe debounce suites best over here, reason behind it is that user may type fast and if we implement throttling then after every interval it will update the UI instead of waiting even if the user is still typing, incase of debouncing the interval will start after the user has stopped typing therefore I think debouncing would be better here.

try {
const { ModelManager, TemplateMarkInterpreter, TemplateMarkTransformer, transform } =
await loadMarkdownProcessingLibs();

const modelManager = new ModelManager({ strict: true });
modelManager.addCTOModel(model, undefined, true);
await modelManager.updateExternalModels();

const engine = new TemplateMarkInterpreter(modelManager, {});
const templateMarkTransformer = new TemplateMarkTransformer();

const templateMarkDom = templateMarkTransformer.fromMarkdownTemplate(
{ content: template },
modelManager,
"contract",
{ verbose: false }
);

const data = JSON.parse(dataString);

const ciceroMark = await engine.generate(templateMarkDom, data);
return await transform(
ciceroMark.toJSON(),
"ciceromark_parsed",
["html"],
{},
{ verbose: false }
);
} catch (error) {
console.error("Error rebuilding the template:", error);
throw new Error("Failed to process the template");
}
}, 500);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 500ms and not 300ms or more than 500ms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already had 500ms from before, I did'nt change the value but It would be a great suggestion checking for lower latency as well.


const getInitialTheme = () => {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('theme');
Expand All @@ -109,14 +131,14 @@ const useAppStore = create<AppState>()(
return {
backgroundColor: initialTheme.backgroundColor,
textColor: initialTheme.textColor,
sampleName: playground.NAME,
templateMarkdown: playground.TEMPLATE,
editorValue: playground.TEMPLATE,
modelCto: playground.MODEL,
editorModelCto: playground.MODEL,
data: JSON.stringify(playground.DATA, null, 2),
editorAgreementData: JSON.stringify(playground.DATA, null, 2),
agreementHtml: "",
sampleName: "",
templateMarkdown: "",
editorValue: "",
modelCto: "",
editorModelCto: "",
data: "",
editorAgreementData: "",
agreementHtml: "",
isAIConfigOpen: false,
isAIChatOpen: false,
error: undefined,
Expand Down Expand Up @@ -152,73 +174,51 @@ const useAppStore = create<AppState>()(
if (compressedData) {
await get().loadFromLink(compressedData);
} else {
await get().rebuild();
await get().loadSample("default");
}
},
loadSample: async (name: string) => {
const sample = SAMPLES.find((s) => s.NAME === name);
if (sample) {
set(() => ({
sampleName: sample.NAME,
agreementHtml: undefined,
error: undefined,
templateMarkdown: sample.TEMPLATE,
editorValue: sample.TEMPLATE,
modelCto: sample.MODEL,
editorModelCto: sample.MODEL,
data: JSON.stringify(sample.DATA, null, 2),
editorAgreementData: JSON.stringify(sample.DATA, null, 2),
}));
await get().rebuild();
}
const playground = await import("../samples/playground");
const sample = SAMPLES.find((s) => s.NAME === name) || playground;
set(() => ({
sampleName: sample.NAME,
agreementHtml: undefined,
error: undefined,
templateMarkdown: sample.TEMPLATE,
editorValue: sample.TEMPLATE,
modelCto: sample.MODEL,
editorModelCto: sample.MODEL,
data: JSON.stringify(sample.DATA, null, 2),
editorAgreementData: JSON.stringify(sample.DATA, null, 2),
}));
await get().rebuild();
},
rebuild: async () => {
const { templateMarkdown, modelCto, data } = get();
try {
const result = await rebuildDeBounce(templateMarkdown, modelCto, data);
set(() => ({ agreementHtml: result, error: undefined })); // Clear error on success
const result = await rebuild(templateMarkdown, modelCto, data);
set(() => ({ agreementHtml: result, error: undefined }));
} catch (error: any) {
set(() => ({ error: formatError(error), isProblemPanelVisible: true }));
}
},
setTemplateMarkdown: async (template: string) => {
set(() => ({ templateMarkdown: template }));
const { modelCto, data } = get();
try {
const result = await rebuildDeBounce(template, modelCto, data);
set(() => ({ agreementHtml: result, error: undefined })); // Clear error on success
} catch (error: any) {
set(() => ({ error: formatError(error), isProblemPanelVisible: true }));
}
await get().rebuild();
},
setEditorValue: (value: string) => {
set(() => ({ editorValue: value }));
},
setModelCto: async (model: string) => {
set(() => ({ modelCto: model }));
const { templateMarkdown, data } = get();
try {
const result = await rebuildDeBounce(templateMarkdown, model, data);
set(() => ({ agreementHtml: result, error: undefined })); // Clear error on success
} catch (error: any) {
set(() => ({ error: formatError(error), isProblemPanelVisible: true }));
}
await get().rebuild();
},
setEditorModelCto: (value: string) => {
set(() => ({ editorModelCto: value }));
},
setData: async (data: string) => {
set(() => ({ data }));
try {
const result = await rebuildDeBounce(
get().templateMarkdown,
get().modelCto,
data
);
set(() => ({ agreementHtml: result, error: undefined })); // Clear error on success
} catch (error: any) {
set(() => ({ error: formatError(error), isProblemPanelVisible: true }));
}
await get().rebuild();
},
setEditorAgreementData: (value: string) => {
set(() => ({ editorAgreementData: value }));
Expand Down
9 changes: 7 additions & 2 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import { defineConfig as defineVitestConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import nodePolyfills from "vite-plugin-node-stdlib-browser";
import { visualizer } from "rollup-plugin-visualizer";

async function getPlugins() {
const chunkSplitPlugin = await import('vite-plugin-chunk-split');
return [chunkSplitPlugin.chunkSplitPlugin()];
}
// https://vitejs.dev/config/
const viteConfig = defineViteConfig({
plugins: [nodePolyfills(), react(), visualizer({
emitFile: true,
filename: "stats.html",
})],
}), getPlugins()],
optimizeDeps: {
include: ["immer"],
include: ["immer", "zustand"],
needsInterop: ['@accordproject/template-engine'],
},
});
Expand Down