diff --git a/emain/emain-platform.ts b/emain/emain-platform.ts index 714316b93e..86b49a07e2 100644 --- a/emain/emain-platform.ts +++ b/emain/emain-platform.ts @@ -193,6 +193,9 @@ ipcMain.on("get-data-dir", (event) => { ipcMain.on("get-config-dir", (event) => { event.returnValue = getWaveConfigDir(); }); +ipcMain.on("get-home-dir", (event) => { + event.returnValue = app.getPath("home"); +}); /** * Gets the value of the XDG_CURRENT_DESKTOP environment variable. If ORIGINAL_XDG_CURRENT_DESKTOP is set, it will be returned instead. diff --git a/emain/preload.ts b/emain/preload.ts index bdde36096f..cc9af57031 100644 --- a/emain/preload.ts +++ b/emain/preload.ts @@ -12,6 +12,7 @@ contextBridge.exposeInMainWorld("api", { getHostName: () => ipcRenderer.sendSync("get-host-name"), getDataDir: () => ipcRenderer.sendSync("get-data-dir"), getConfigDir: () => ipcRenderer.sendSync("get-config-dir"), + getHomeDir: () => ipcRenderer.sendSync("get-home-dir"), getAboutModalDetails: () => ipcRenderer.sendSync("get-about-modal-details"), getDocsiteUrl: () => ipcRenderer.sendSync("get-docsite-url"), getWebviewPreload: () => ipcRenderer.sendSync("get-webview-preload"), diff --git a/frontend/app/view/codeeditor/schemaendpoints.ts b/frontend/app/view/codeeditor/schemaendpoints.ts index dd6068c477..5056f7121e 100644 --- a/frontend/app/view/codeeditor/schemaendpoints.ts +++ b/frontend/app/view/codeeditor/schemaendpoints.ts @@ -10,11 +10,34 @@ type EndpointInfo = { schema: object; }; +function prependWildcard(path: string): string { + return path.startsWith("/") ? `*${path}` : `*/${path}`; +} + +function convertToTildePath(absolutePath: string): string { + const homeDir = getApi().getHomeDir(); + if (absolutePath.startsWith(homeDir)) { + return "~" + absolutePath.slice(homeDir.length); + } + return absolutePath; +} + +function makeConfigPathMatches(suffix: string): Array { + const configPath = `${getApi().getConfigDir()}${suffix}`; + const tildePath = convertToTildePath(configPath); + const paths = [configPath, prependWildcard(configPath)]; + if (tildePath !== configPath) { + paths.push(tildePath); + paths.push(prependWildcard(tildePath)); + } + return paths; +} + const allFilepaths: Map> = new Map(); -allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, [`${getApi().getConfigDir()}/settings.json`]); -allFilepaths.set(`${getWebServerEndpoint()}/schema/connections.json`, [`${getApi().getConfigDir()}/connections.json`]); -allFilepaths.set(`${getWebServerEndpoint()}/schema/aipresets.json`, [`${getApi().getConfigDir()}/presets/ai.json`]); -allFilepaths.set(`${getWebServerEndpoint()}/schema/widgets.json`, [`${getApi().getConfigDir()}/widgets.json`]); +allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, makeConfigPathMatches("/settings.json")); +allFilepaths.set(`${getWebServerEndpoint()}/schema/connections.json`, makeConfigPathMatches("/connections.json")); +allFilepaths.set(`${getWebServerEndpoint()}/schema/aipresets.json`, makeConfigPathMatches("/presets/ai.json")); +allFilepaths.set(`${getWebServerEndpoint()}/schema/widgets.json`, makeConfigPathMatches("/widgets.json")); async function getSchemaEndpointInfo(endpoint: string): Promise { let schema: Object; diff --git a/frontend/types/custom.d.ts b/frontend/types/custom.d.ts index 3c5c26650a..23c51a9b95 100644 --- a/frontend/types/custom.d.ts +++ b/frontend/types/custom.d.ts @@ -83,6 +83,7 @@ declare global { getHostName: () => string; // get-host-name getDataDir: () => string; // get-data-dir getConfigDir: () => string; // get-config-dir + getHomeDir: () => string; // get-home-dir getWebviewPreload: () => string; // get-webview-preload getAboutModalDetails: () => AboutModalDetails; // get-about-modal-details getDocsiteUrl: () => string; // get-docsite-url diff --git a/pkg/aiusechat/openai/openai-convertmessage.go b/pkg/aiusechat/openai/openai-convertmessage.go index 78f91e7b5d..31cbe1fd04 100644 --- a/pkg/aiusechat/openai/openai-convertmessage.go +++ b/pkg/aiusechat/openai/openai-convertmessage.go @@ -6,7 +6,9 @@ package openai import ( "bytes" "context" + "crypto/sha256" "encoding/base64" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -58,6 +60,16 @@ func extractXmlAttribute(tag, attrName string) (string, bool) { return value, true } +// generateDeterministicSuffix creates an 8-character hash from input strings +func generateDeterministicSuffix(inputs ...string) string { + hasher := sha256.New() + for _, input := range inputs { + hasher.Write([]byte(input)) + } + hash := hasher.Sum(nil) + return hex.EncodeToString(hash)[:8] +} + // ---------- OpenAI Request Types ---------- type StreamOptionsType struct { @@ -388,8 +400,8 @@ func convertFileAIMessagePart(part uctypes.AIMessagePart) (*OpenAIMessageContent encodedFileName := strings.ReplaceAll(fileName, `"`, """) quotedFileName := strconv.Quote(encodedFileName) - randomSuffix := uuid.New().String()[0:8] - formattedText := fmt.Sprintf("\n%s\n", randomSuffix, quotedFileName, textContent, randomSuffix) + deterministicSuffix := generateDeterministicSuffix(textContent, fileName) + formattedText := fmt.Sprintf("\n%s\n", deterministicSuffix, quotedFileName, textContent, deterministicSuffix) return &OpenAIMessageContent{ Type: "input_text", @@ -412,8 +424,8 @@ func convertFileAIMessagePart(part uctypes.AIMessagePart) (*OpenAIMessageContent encodedDirName := strings.ReplaceAll(directoryName, `"`, """) quotedDirName := strconv.Quote(encodedDirName) - randomSuffix := uuid.New().String()[0:8] - formattedText := fmt.Sprintf("\n%s\n", randomSuffix, quotedDirName, jsonContent, randomSuffix) + deterministicSuffix := generateDeterministicSuffix(jsonContent, directoryName) + formattedText := fmt.Sprintf("\n%s\n", deterministicSuffix, quotedDirName, jsonContent, deterministicSuffix) return &OpenAIMessageContent{ Type: "input_text",