This commit is contained in:
zsviczian
2023-11-26 18:38:31 +01:00
parent 8b1daed0ef
commit 9f8a9bfa8a
54 changed files with 1211 additions and 397 deletions

129
src/utils/AIUtils.ts Normal file
View File

@@ -0,0 +1,129 @@
import { Notice, RequestUrlResponse, requestUrl } from "obsidian";
import ExcalidrawPlugin from "src/main";
type MessageContent =
| string
| (string | { type: "image_url"; image_url: string })[];
export type GPTCompletionRequest = {
model: string;
messages: {
role: "system" | "user" | "assistant" | "function";
content: MessageContent;
name?: string | undefined;
}[];
functions?: any[] | undefined;
function_call?: any | undefined;
stream?: boolean | undefined;
temperature?: number | undefined;
top_p?: number | undefined;
max_tokens?: number | undefined;
n?: number | undefined;
best_of?: number | undefined;
frequency_penalty?: number | undefined;
presence_penalty?: number | undefined;
logit_bias?:
| {
[x: string]: number;
}
| undefined;
stop?: (string[] | string) | undefined;
};
export type AIRequest = {
image?: string;
text?: string;
instruction?: string;
systemPrompt?: string;
};
export const postOpenAI = async (request: AIRequest) : Promise<RequestUrlResponse> => {
const plugin: ExcalidrawPlugin = window.ExcalidrawAutomate.plugin;
const { openAIAPIToken, openAIDefaultTextModel, openAIDefaultVisionModel} = plugin.settings;
const { image, text, instruction, systemPrompt } = request;
const requestType = image ? "image" : "text";
let body: GPTCompletionRequest;
if(openAIAPIToken === "") {
new Notice("OpenAI API Token is not set. Please set it in plugin settings.");
return null;
}
switch (requestType) {
case "text":
body = {
model: openAIDefaultTextModel,
max_tokens: 4096,
messages: [
...(systemPrompt ? [{role: "system" as const,content: systemPrompt}] : []),
{
role: "user",
content: text,
},
...(instruction ? [{role: "user" as const,content: instruction}] : [])
],
};
break;
case "image":
body = {
model: openAIDefaultVisionModel,
max_tokens: 4096,
messages: [
...(systemPrompt ? [{role: "system" as const,content: [systemPrompt]}] : []),
{
role: "user",
content: [
{
type: "image_url",
image_url: image,
},
...(instruction ? [instruction] : []), //"Turn this into a single html file using tailwind.",
],
},
],
};
break;
default:
return null;
}
try {
const resp = await requestUrl ({
url: "https://api.openai.com/v1/chat/completions",
method: "post",
contentType: "application/json",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${openAIAPIToken}`,
},
throw: false
});
return resp;
} catch (e) {
console.log(e);
}
return null;
}
/**
* Grabs the codeblock contents from the supplied markdown string.
* @param markdown
* @param codeblockType
* @returns an array of dictionaries with the codeblock contents and type
*/
export const extractCodeBlocks = (markdown: string): { data: string, type: string }[] => {
if (!markdown) return [];
markdown = markdown.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
const result: { data: string, type: string }[] = [];
const regex = /```([a-zA-Z0-9]*)\n([\s\S]+?)```/g;
let match;
while ((match = regex.exec(markdown)) !== null) {
const codeblockType = match[1]??"";
const codeblockString = match[2].trim();
result.push({ data: codeblockString, type: codeblockType });
}
return result;
}

View File

@@ -11,6 +11,7 @@ container.appendChild(node.contentEl)
import { TFile, WorkspaceLeaf, WorkspaceSplit } from "obsidian";
import ExcalidrawView from "src/ExcalidrawView";
import { getContainerForDocument, ConstructableWorkspaceSplit, isObsidianThemeDark } from "./ObsidianUtils";
import { CustomMutationObserver, isDebugMode } from "./DebugHelper";
declare module "obsidian" {
interface Workspace {
@@ -94,8 +95,8 @@ export class CanvasNodeFactory {
if (!node.child.editor?.containerEl?.parentElement?.parentElement) return;
node.child.editor.containerEl.parentElement.parentElement.classList.remove(obsidianTheme);
node.child.editor.containerEl.parentElement.parentElement.classList.add(theme);
const observer = new MutationObserver((mutationsList) => {
const nodeObserverFn: MutationCallback = (mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const targetElement = mutation.target as HTMLElement;
@@ -105,7 +106,10 @@ export class CanvasNodeFactory {
}
}
}
});
};
const observer = isDebugMode
? new CustomMutationObserver(nodeObserverFn, "CanvasNodeFactory")
: new MutationObserver(nodeObserverFn);
observer.observe(node.child.editor.containerEl.parentElement.parentElement, { attributes: true });
})();

View File

@@ -1,12 +1,12 @@
import { NonDeletedExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
import { DEVICE, REG_LINKINDEX_INVALIDCHARS } from "src/constants";
import { DEVICE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/constants";
import { getParentOfClass } from "./ObsidianUtils";
import { TFile, WorkspaceLeaf } from "obsidian";
import { getLinkParts } from "./Utils";
import ExcalidrawView from "src/ExcalidrawView";
export const useDefaultExcalidrawFrame = (element: NonDeletedExcalidrawElement) => {
return !element.link.startsWith("["); // && !element.link.match(TWITTER_REG);
return !(element.link.startsWith("[") || element.link.startsWith("file:") || element.link.startsWith("data:")); // && !element.link.match(TWITTER_REG);
}
export const leafMap = new Map<string, WorkspaceLeaf>();

38
src/utils/DebugHelper.ts Normal file
View File

@@ -0,0 +1,38 @@
export const isDebugMode = false;
export const durationTreshold = 0.05; //ms
export class CustomMutationObserver {
private originalCallback: MutationCallback;
private observer: MutationObserver | null;
private name: string;
constructor(callback: MutationCallback, name: string) {
this.originalCallback = callback;
this.observer = null;
this.name = name;
}
observe(target: Node, options: MutationObserverInit) {
const wrappedCallback: MutationCallback = async (mutationsList, observer) => {
const startTime = performance.now(); // Get start time
await this.originalCallback(mutationsList, observer); // Invoke the original callback
const endTime = performance.now(); // Get end time
const executionTime = endTime - startTime;
if (executionTime > durationTreshold) {
console.log(`Excalidraw ${this.name} MutationObserver callback took ${executionTime}ms to execute`);
}
};
this.observer = new MutationObserver(wrappedCallback);
// Start observing with the modified callback
this.observer.observe(target, options);
}
disconnect() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
}
}

View File

@@ -6,7 +6,7 @@ import { DynamicStyle } from "src/types";
import { cloneElement } from "src/ExcalidrawAutomate";
import { ExcalidrawFrameElement } from "@zsviczian/excalidraw/types/element/types";
import { addAppendUpdateCustomData } from "./Utils";
import { mutateElement } from "src/constants";
import { mutateElement } from "src/constants/constants";
export const setDynamicStyle = (
ea: ExcalidrawAutomate,

View File

@@ -1,5 +1,5 @@
import { MAX_IMAGE_SIZE, IMAGE_TYPES } from "src/constants";
import { MAX_IMAGE_SIZE, IMAGE_TYPES } from "src/constants/constants";
import { TFile } from "obsidian";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";

View File

@@ -1,6 +1,6 @@
import { DataURL } from "@zsviczian/excalidraw/types/types";
import { loadPdfJs, normalizePath, Notice, requestUrl, RequestUrlResponse, TAbstractFile, TFile, TFolder, Vault } from "obsidian";
import { URLFETCHTIMEOUT } from "src/constants";
import { URLFETCHTIMEOUT } from "src/constants/constants";
import { MimeType } from "src/EmbeddedFileLoader";
import { ExcalidrawSettings } from "src/settings";
import { errorlog, getDataURL } from "./Utils";
@@ -134,7 +134,7 @@ export function getEmbedFilename(
* Open or create a folderpath if it does not exist
* @param folderpath
*/
export async function checkAndCreateFolder(folderpath: string) {
export async function checkAndCreateFolder(folderpath: string):Promise<TFolder> {
const vault = app.vault;
folderpath = normalizePath(folderpath);
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/658
@@ -146,7 +146,7 @@ export async function checkAndCreateFolder(folderpath: string) {
if (folder && folder instanceof TFile) {
new Notice(`The folder cannot be created because it already exists as a file: ${folderpath}.`)
}
await vault.createFolder(folderpath);
return await vault.createFolder(folderpath);
}
export const getURLImageExtension = (url: string):string => {

View File

@@ -1,4 +1,4 @@
import { DEVICE, isDarwin } from "src/constants";
import { DEVICE, isDarwin } from "src/constants/constants";
export type ModifierKeys = {shiftKey:boolean, ctrlKey: boolean, metaKey: boolean, altKey: boolean};
export type KeyEvent = PointerEvent | MouseEvent | KeyboardEvent | React.DragEvent | React.PointerEvent | React.MouseEvent | ModifierKeys;
export type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";

View File

@@ -5,7 +5,7 @@ import {
import ExcalidrawPlugin from "../main";
import { checkAndCreateFolder, splitFolderAndFilename } from "./FileUtils";
import { linkClickModifierType, ModifierKeys } from "./ModifierkeyHelper";
import { REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN } from "src/constants";
import { REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN } from "src/constants/constants";
export const getParentOfClass = (element: Element, cssClass: string):HTMLElement | null => {
let parent = element.parentElement;

View File

@@ -11,6 +11,8 @@ export class StylesManager {
private styleDark: string;
private plugin: ExcalidrawPlugin;
constructor(plugin: ExcalidrawPlugin) {
this.plugin = plugin;
plugin.app.workspace.onLayoutReady(async () => {

View File

@@ -13,7 +13,7 @@ import {
ASSISTANT_FONT,
CASCADIA_FONT,
VIRGIL_FONT,
} from "src/constFonts";
} from "src/constants/constFonts";
import {
FRONTMATTER_KEY_EXPORT_DARK,
FRONTMATTER_KEY_EXPORT_TRANSPARENT,
@@ -23,7 +23,7 @@ import {
exportToSvg,
exportToBlob,
IMAGE_TYPES
} from "../constants";
} from "../constants/constants";
import ExcalidrawPlugin from "../main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
import { ExportSettings } from "../ExcalidrawView";